<!DOCTYPE html><html><head>
<meta charset="utf-8">
<title>Coverage</title><script>

headings = [];

onload = function(){
  headings = document.querySelectorAll('h2');
};

onscroll = function(e){
  var heading = find(window.scrollY);
  if (!heading) return;
  var links = document.querySelectorAll('#menu a')
    , link;

  for (var i = 0, len = links.length; i < len; ++i) {
    link = links[i];
    link.className = link.getAttribute('href') == '#' + heading.id
      ? 'active'
      : '';
  }
};

function find(y) {
  var i = headings.length
    , heading;

  while (i--) {
    heading = headings[i];
    if (y >= heading.offsetTop) {
      return heading;
    }
  }
}
</script>
<style>

body {
  font: 14px/1.6 "Helvetica Neue", Helvetica, Arial, sans-serif;
  margin: 0;
  color: #2C2C2C;
  border-top: 2px solid #ddd;
}

#coverage {
  padding: 60px;
}

h1 a {
  color: inherit;
  font-weight: inherit;
}

h1 a:hover {
  text-decoration: none;
}

.onload h1 {
  opacity: 1;
}

h2 {
  width: 80%;
  margin-top: 80px;
  margin-bottom: 0;
  font-weight: 100;
  letter-spacing: 1px;
  border-bottom: 1px solid #eee;
}

a {
  color: #8A6343;
  font-weight: bold;
  text-decoration: none;
}

a:hover {
  text-decoration: underline;
}

ul {
  margin-top: 20px;
  padding: 0 15px;
  width: 100%;
}

ul li {
  float: left;
  width: 40%;
  margin-top: 5px;
  margin-right: 60px;
  list-style: none;
  border-bottom: 1px solid #eee;
  padding: 5px 0;
  font-size: 12px;
}

ul::after {
  content: '.';
  height: 0;
  display: block;
  visibility: hidden;
  clear: both;
}

code {
  font: 12px monaco, monospace;
}

pre {
  margin: 30px;
  padding: 30px;
  border: 1px solid #eee;
  border-bottom-color: #ddd;
  -webkit-border-radius: 2px;
  -moz-border-radius: 2px;
  border-radius: 2px;
  -webkit-box-shadow: inset 0 0 10px #eee;
  -moz-box-shadow: inset 0 0 10px #eee;
  box-shadow: inset 0 0 10px #eee;
  overflow-x: auto;
}

img {
  margin: 30px;
  padding: 1px;
  -webkit-border-radius: 3px;
  -moz-border-radius: 3px;
  border-radius: 3px;
  -webkit-box-shadow: 0 3px 10px #dedede, 0 1px 5px #888;
  -moz-box-shadow: 0 3px 10px #dedede, 0 1px 5px #888;
  box-shadow: 0 3px 10px #dedede, 0 1px 5px #888;
  max-width: 100%;
}

footer {
  background: #eee;
  width: 100%;
  padding: 50px 0;
  text-align: right;
  border-top: 1px solid #ddd;
}

footer span {
  display: block;
  margin-right: 30px;
  color: #888;
  font-size: 12px;
}

#menu {
  position: fixed;
  font-size: 12px;
  overflow-y: auto;
  top: 0;
  right: 0;
  margin: 0;
  height: 100%;
  padding: 15px 0;
  text-align: right;
  border-left: 1px solid #eee;
  -moz-box-shadow: 0 0 2px #888
     , inset 5px 0 20px rgba(0,0,0,.5)
     , inset 5px 0 3px rgba(0,0,0,.3);
  -webkit-box-shadow: 0 0 2px #888
     , inset 5px 0 20px rgba(0,0,0,.5)
     , inset 5px 0 3px rgba(0,0,0,.3);
  box-shadow: 0 0 2px #888
     , inset 5px 0 20px rgba(0,0,0,.5)
     , inset 5px 0 3px rgba(0,0,0,.3);
  -webkit-font-smoothing: antialiased;
  background: url("");
}

#menu::after {
  display: block;
  content: '';
  padding-top: 80px;
}

#logo {
  position: fixed;
  bottom: 10px;
  right: 10px;
  background: rgba(255,255,255,.1);
  font-size: 11px;
  display: block;
  width: 20px;
  height: 20px;
  line-height: 20px;
  text-align: center;
  -webkit-border-radius: 20px;
  -moz-border-radius: 20px;
  border-radius: 20px;
  -webkit-box-shadow: 0 0 3px rgba(0,0,0,.2);
  -moz-box-shadow: 0 0 3px rgba(0,0,0,.2);
  box-shadow: 0 0 3px rgba(0,0,0,.2);
  color: inherit;
}

#menu li a {
  display: block;
  color: white;
  padding: 0 35px 0 25px;
  -webkit-transition: background 300ms;
  -moz-transition: background 300ms;
}

#menu li {
  position: relative;
  list-style: none;
}

#menu a:hover,
#menu a.active {
  text-decoration: none;
  background: rgba(255,255,255,.1);
}

#menu li:hover .cov {
  opacity: 1;
}

#menu li .dirname {
  opacity: .60;
  padding-right: 2px;
}

#menu li .basename {
  opacity: 1;
}

#menu .cov {
  background: rgba(0,0,0,.4);
  position: absolute;
  top: 0;
  right: 8px;
  font-size: 9px;
  opacity: .6;
  text-align: left;
  width: 17px;
  -webkit-border-radius: 10px;
  -moz-border-radius: 10px;
  border-radius: 10px;
  padding: 2px 3px;
  text-align: center;
}

#stats:nth-child(2n) {
  display: inline-block;
  margin-top: 15px;
  border: 1px solid #eee;
  padding: 10px;
  -webkit-box-shadow: inset 0 0 2px #eee;
  -moz-box-shadow: inset 0 0 2px #eee;
  box-shadow: inset 0 0 2px #eee;
  -webkit-border-radius: 5px;
  -moz-border-radius: 5px;
  border-radius: 5px;
}

#stats div {
  float: left;
  padding: 0 5px;
}

#stats::after {
  display: block;
  content: '';
  clear: both;
}

#stats .sloc::after {
  content: ' SLOC';
  color: #b6b6b6;
}

#stats .percentage::after {
  content: ' coverage';
  color: #b6b6b6;
}

#stats .hits,
#stats .misses {
  display: none;
}

.high {
  color: #00d4b4;
}
.medium {
  color: #e87d0d;
}
.low {
  color: #d4081a;
}
.terrible {
  color: #d4081a;
  font-weight: bold;
}

table {
  width: 80%;
  margin-top: 10px;
  border-collapse: collapse;
  border: 1px solid #cbcbcb;
  color: #363636;
  -webkit-border-radius: 3px;
  -moz-border-radius: 3px;
  border-radius: 3px;
}

table thead {
  display: none;
}

table td.line,
table td.hits {
  width: 20px;
  background: #eaeaea;
  text-align: center;
  font-size: 11px;
  padding: 0 10px;
  color: #949494;
}

table td.hits {
  width: 10px;
  padding: 2px 5px;
  color: rgba(0,0,0,.2);
  background: #f0f0f0;
}

tr.miss td.line,
tr.miss td.hits {
  background: #e6c3c7;
}

tr.miss td {
  background: #f8d5d8;
}

td.source {
  padding-left: 15px;
  line-height: 15px;
  white-space: pre;
  font: 12px monaco, monospace;
}

code .comment { color: #ddd }
code .init { color: #2F6FAD }
code .string { color: #5890AD }
code .keyword { color: #8A6343 }
code .number { color: #2F6FAD }
</style></head><body><div id="coverage"><h1 id="overview">Coverage</h1><div id="menu"><li><a href="#overview">overview</a></li><li><span class="cov medium">73</span><a href="#/Users/chenboxiang/WebstormProjects/fdfs-client/lib/fdfs.js"><span class="dirname">/Users/chenboxiang/WebstormProjects/fdfs-client/lib/</span><span class="basename">fdfs.js</span></a></li><li><span class="cov high">93</span><a href="#/Users/chenboxiang/WebstormProjects/fdfs-client/lib/helpers.js"><span class="dirname">/Users/chenboxiang/WebstormProjects/fdfs-client/lib/</span><span class="basename">helpers.js</span></a></li><li><span class="cov high">100</span><a href="#/Users/chenboxiang/WebstormProjects/fdfs-client/lib/logger.js"><span class="dirname">/Users/chenboxiang/WebstormProjects/fdfs-client/lib/</span><span class="basename">logger.js</span></a></li><li><span class="cov high">87</span><a href="#/Users/chenboxiang/WebstormProjects/fdfs-client/lib/protocol.js"><span class="dirname">/Users/chenboxiang/WebstormProjects/fdfs-client/lib/</span><span class="basename">protocol.js</span></a></li><li><span class="cov high">91</span><a href="#/Users/chenboxiang/WebstormProjects/fdfs-client/lib/storage.js"><span class="dirname">/Users/chenboxiang/WebstormProjects/fdfs-client/lib/</span><span class="basename">storage.js</span></a></li><li><span class="cov high">92</span><a href="#/Users/chenboxiang/WebstormProjects/fdfs-client/lib/tracker.js"><span class="dirname">/Users/chenboxiang/WebstormProjects/fdfs-client/lib/</span><span class="basename">tracker.js</span></a></li><a id="logo" href="http://visionmedia.github.io/mocha/">m</a></div><div id="stats" class="high"><div class="percentage">86%</div><div class="sloc">566</div><div class="hits">490</div><div class="misses">76</div></div><div id="files"><div class="file"><h2 id="/Users/chenboxiang/WebstormProjects/fdfs-client/lib/fdfs.js">/Users/chenboxiang/WebstormProjects/fdfs-client/lib/fdfs.js</h2><div id="stats" class="medium"><div class="percentage">73%</div><div class="sloc">144</div><div class="hits">106</div><div class="misses">38</div></div><table id="source"><thead><tr><th>Line</th><th>Hits</th><th>Source</th></tr></thead><tbody><tr><td class="line">1</td><td class="hits"></td><td class="source">/**</td></tr><tr><td class="line">2</td><td class="hits"></td><td class="source"> * Author: chenboxiang</td></tr><tr><td class="line">3</td><td class="hits"></td><td class="source"> * Date: 14-6-13</td></tr><tr><td class="line">4</td><td class="hits"></td><td class="source"> * Time: 下午8:42</td></tr><tr><td class="line">5</td><td class="hits"></td><td class="source"> */</td></tr><tr class="hit"><td class="line">6</td><td class="hits">1</td><td class="source">'use strict'</td></tr><tr><td class="line">7</td><td class="hits"></td><td class="source"> </td></tr><tr class="hit"><td class="line">8</td><td class="hits">1</td><td class="source">var _ = require('lodash')</td></tr><tr class="hit"><td class="line">9</td><td class="hits">1</td><td class="source">var Tracker = require('./tracker')</td></tr><tr class="hit"><td class="line">10</td><td class="hits">1</td><td class="source">var EventEmitter = require('events').EventEmitter</td></tr><tr class="hit"><td class="line">11</td><td class="hits">1</td><td class="source">var util = require('util')</td></tr><tr class="hit"><td class="line">12</td><td class="hits">1</td><td class="source">var logger = require('./logger')</td></tr><tr class="hit"><td class="line">13</td><td class="hits">1</td><td class="source">var fs = require('fs')</td></tr><tr class="hit"><td class="line">14</td><td class="hits">1</td><td class="source">var is = require('is-type-of')</td></tr><tr class="hit"><td class="line">15</td><td class="hits">1</td><td class="source">var path = require('path')</td></tr><tr class="hit"><td class="line">16</td><td class="hits">1</td><td class="source">var helpers = require('./helpers')</td></tr><tr class="hit"><td class="line">17</td><td class="hits">1</td><td class="source">var protocol = require('./protocol')</td></tr><tr><td class="line">18</td><td class="hits"></td><td class="source"> </td></tr><tr class="hit"><td class="line">19</td><td class="hits">1</td><td class="source">var defaults = {</td></tr><tr><td class="line">20</td><td class="hits"></td><td class="source">    charset: 'utf8',</td></tr><tr><td class="line">21</td><td class="hits"></td><td class="source">    trackers: [],</td></tr><tr><td class="line">22</td><td class="hits"></td><td class="source">    // 默认超时时间10s</td></tr><tr><td class="line">23</td><td class="hits"></td><td class="source">    timeout: 10000,</td></tr><tr><td class="line">24</td><td class="hits"></td><td class="source">    // 默认后缀</td></tr><tr><td class="line">25</td><td class="hits"></td><td class="source">    // 当获取不到文件后缀时使用</td></tr><tr><td class="line">26</td><td class="hits"></td><td class="source">    defaultExt: 'txt'</td></tr><tr><td class="line">27</td><td class="hits"></td><td class="source">}</td></tr><tr><td class="line">28</td><td class="hits"></td><td class="source"> </td></tr><tr class="hit"><td class="line">29</td><td class="hits">1</td><td class="source">function FdfsClient(config) {</td></tr><tr class="hit"><td class="line">30</td><td class="hits">1</td><td class="source">    EventEmitter.call(this)</td></tr><tr><td class="line">31</td><td class="hits"></td><td class="source">    // config global logger</td></tr><tr class="hit"><td class="line">32</td><td class="hits">1</td><td class="source">    if (config.logger) {</td></tr><tr class="hit"><td class="line">33</td><td class="hits">1</td><td class="source">        logger.setLogger(config.logger)</td></tr><tr><td class="line">34</td><td class="hits"></td><td class="source">    }</td></tr><tr class="hit"><td class="line">35</td><td class="hits">1</td><td class="source">    this.config = _.extend({}, defaults, config)</td></tr><tr><td class="line">36</td><td class="hits"></td><td class="source"> </td></tr><tr class="hit"><td class="line">37</td><td class="hits">1</td><td class="source">    this._checkConfig()</td></tr><tr class="hit"><td class="line">38</td><td class="hits">1</td><td class="source">    this._init()</td></tr><tr class="hit"><td class="line">39</td><td class="hits">1</td><td class="source">    this._errorHandle()</td></tr><tr><td class="line">40</td><td class="hits"></td><td class="source">}</td></tr><tr><td class="line">41</td><td class="hits"></td><td class="source"> </td></tr><tr><td class="line">42</td><td class="hits"></td><td class="source">// extends from EventEmitter</td></tr><tr class="hit"><td class="line">43</td><td class="hits">1</td><td class="source">util.inherits(FdfsClient, EventEmitter)</td></tr><tr><td class="line">44</td><td class="hits"></td><td class="source"> </td></tr><tr><td class="line">45</td><td class="hits"></td><td class="source">// ------------- private methods</td></tr><tr><td class="line">46</td><td class="hits"></td><td class="source">/**</td></tr><tr><td class="line">47</td><td class="hits"></td><td class="source"> * 确认配置是否合法</td></tr><tr><td class="line">48</td><td class="hits"></td><td class="source"> * @private</td></tr><tr><td class="line">49</td><td class="hits"></td><td class="source"> */</td></tr><tr class="hit"><td class="line">50</td><td class="hits">1</td><td class="source">FdfsClient.prototype._checkConfig = function() {</td></tr><tr><td class="line">51</td><td class="hits"></td><td class="source"> </td></tr><tr><td class="line">52</td><td class="hits"></td><td class="source">    // ------------- 验证trackers是否合法</td></tr><tr class="hit"><td class="line">53</td><td class="hits">1</td><td class="source">    if (!this.config.trackers) {</td></tr><tr class="miss"><td class="line">54</td><td class="hits">0</td><td class="source">        throw new Error('you must specify &quot;trackers&quot; in config.')</td></tr><tr><td class="line">55</td><td class="hits"></td><td class="source">    }</td></tr><tr><td class="line">56</td><td class="hits"></td><td class="source"> </td></tr><tr class="hit"><td class="line">57</td><td class="hits">1</td><td class="source">    if (!Array.isArray(this.config.trackers)) {</td></tr><tr class="miss"><td class="line">58</td><td class="hits">0</td><td class="source">        this.config.trackers = [this.config.trackers]</td></tr><tr><td class="line">59</td><td class="hits"></td><td class="source">    }</td></tr><tr><td class="line">60</td><td class="hits"></td><td class="source"> </td></tr><tr class="hit"><td class="line">61</td><td class="hits">1</td><td class="source">    if (this.config.trackers.length === 0) {</td></tr><tr class="miss"><td class="line">62</td><td class="hits">0</td><td class="source">        throw new Error('&quot;trackers&quot; in config is empty.')</td></tr><tr><td class="line">63</td><td class="hits"></td><td class="source">    }</td></tr><tr><td class="line">64</td><td class="hits"></td><td class="source"> </td></tr><tr class="hit"><td class="line">65</td><td class="hits">1</td><td class="source">    this.config.trackers.forEach(function(tracker) {</td></tr><tr class="hit"><td class="line">66</td><td class="hits">1</td><td class="source">        if (!tracker.host || !tracker.port) {</td></tr><tr class="miss"><td class="line">67</td><td class="hits">0</td><td class="source">            throw new Error('&quot;trackers&quot; in config is invalid, every tracker must all have &quot;host&quot; and &quot;port&quot;.')</td></tr><tr><td class="line">68</td><td class="hits"></td><td class="source">        }</td></tr><tr><td class="line">69</td><td class="hits"></td><td class="source">    })</td></tr><tr><td class="line">70</td><td class="hits"></td><td class="source">}</td></tr><tr><td class="line">71</td><td class="hits"></td><td class="source"> </td></tr><tr class="hit"><td class="line">72</td><td class="hits">1</td><td class="source">FdfsClient.prototype._init = function() {</td></tr><tr><td class="line">73</td><td class="hits"></td><td class="source">    // --------- init trackers</td></tr><tr class="hit"><td class="line">74</td><td class="hits">1</td><td class="source">    var self = this</td></tr><tr class="hit"><td class="line">75</td><td class="hits">1</td><td class="source">    this._trackers = []</td></tr><tr class="hit"><td class="line">76</td><td class="hits">1</td><td class="source">    this.config.trackers.forEach(function(tc) {</td></tr><tr class="hit"><td class="line">77</td><td class="hits">1</td><td class="source">        tc.timeout = self.config.timeout</td></tr><tr class="hit"><td class="line">78</td><td class="hits">1</td><td class="source">        tc.charset = self.config.charset</td></tr><tr class="hit"><td class="line">79</td><td class="hits">1</td><td class="source">        var tracker = new Tracker(tc)</td></tr><tr class="hit"><td class="line">80</td><td class="hits">1</td><td class="source">        self._trackers.push(tracker)</td></tr><tr class="hit"><td class="line">81</td><td class="hits">1</td><td class="source">        tracker.on('error', function(err) {</td></tr><tr class="miss"><td class="line">82</td><td class="hits">0</td><td class="source">            logger.error(err)</td></tr><tr><td class="line">83</td><td class="hits"></td><td class="source">            // 将有错误的tracker剔除</td></tr><tr class="miss"><td class="line">84</td><td class="hits">0</td><td class="source">            self._trackers.splice(self._trackers.indexOf(tracker), 1)</td></tr><tr><td class="line">85</td><td class="hits"></td><td class="source">            // 检查是否还有可用的tracker</td></tr><tr class="miss"><td class="line">86</td><td class="hits">0</td><td class="source">            if (self._trackers.length === 0) {</td></tr><tr class="miss"><td class="line">87</td><td class="hits">0</td><td class="source">                self.emit('error', new Error('There are no available trackers, please check your tracker config or your tracker server.'));</td></tr><tr><td class="line">88</td><td class="hits"></td><td class="source">            }</td></tr><tr><td class="line">89</td><td class="hits"></td><td class="source">        })</td></tr><tr><td class="line">90</td><td class="hits"></td><td class="source">    })</td></tr><tr><td class="line">91</td><td class="hits"></td><td class="source"> </td></tr><tr><td class="line">92</td><td class="hits"></td><td class="source">}</td></tr><tr><td class="line">93</td><td class="hits"></td><td class="source"> </td></tr><tr class="hit"><td class="line">94</td><td class="hits">1</td><td class="source">FdfsClient.prototype._errorHandle = function() {</td></tr><tr><td class="line">95</td><td class="hits"></td><td class="source">    // 1. 当没有tracker可用时触发</td></tr><tr><td class="line">96</td><td class="hits"></td><td class="source">    // 2. 当连接storage错误时触发</td></tr><tr class="hit"><td class="line">97</td><td class="hits">1</td><td class="source">    this.on('error', function(err) {</td></tr><tr class="miss"><td class="line">98</td><td class="hits">0</td><td class="source">        logger.error(err)</td></tr><tr><td class="line">99</td><td class="hits"></td><td class="source">    })</td></tr><tr><td class="line">100</td><td class="hits"></td><td class="source">}</td></tr><tr><td class="line">101</td><td class="hits"></td><td class="source"> </td></tr><tr><td class="line">102</td><td class="hits"></td><td class="source">/**</td></tr><tr><td class="line">103</td><td class="hits"></td><td class="source"> * 按顺序获取可用的tracker</td></tr><tr><td class="line">104</td><td class="hits"></td><td class="source"> * @private</td></tr><tr><td class="line">105</td><td class="hits"></td><td class="source"> */</td></tr><tr class="hit"><td class="line">106</td><td class="hits">1</td><td class="source">FdfsClient.prototype._getTracker = function() {</td></tr><tr class="hit"><td class="line">107</td><td class="hits">27</td><td class="source">    if (null == this._trackerIndex) {</td></tr><tr class="hit"><td class="line">108</td><td class="hits">1</td><td class="source">        this._trackerIndex = 0</td></tr><tr class="hit"><td class="line">109</td><td class="hits">1</td><td class="source">        return this._trackers[0]</td></tr><tr><td class="line">110</td><td class="hits"></td><td class="source"> </td></tr><tr><td class="line">111</td><td class="hits"></td><td class="source">    } else {</td></tr><tr class="hit"><td class="line">112</td><td class="hits">26</td><td class="source">        this._trackerIndex++</td></tr><tr class="hit"><td class="line">113</td><td class="hits">26</td><td class="source">        if (this._trackerIndex &gt;= this._trackers.length) {</td></tr><tr class="hit"><td class="line">114</td><td class="hits">26</td><td class="source">            this._trackerIndex = 0</td></tr><tr><td class="line">115</td><td class="hits"></td><td class="source">        }</td></tr><tr class="hit"><td class="line">116</td><td class="hits">26</td><td class="source">        return this._trackers[this._trackerIndex]</td></tr><tr><td class="line">117</td><td class="hits"></td><td class="source">    }</td></tr><tr><td class="line">118</td><td class="hits"></td><td class="source">}</td></tr><tr><td class="line">119</td><td class="hits"></td><td class="source"> </td></tr><tr class="hit"><td class="line">120</td><td class="hits">1</td><td class="source">FdfsClient.prototype._upload = function(file, options, callback) {</td></tr><tr class="hit"><td class="line">121</td><td class="hits">8</td><td class="source">    var tracker = this._getTracker()</td></tr><tr><td class="line">122</td><td class="hits"></td><td class="source"> </td></tr><tr class="hit"><td class="line">123</td><td class="hits">8</td><td class="source">    tracker.getStoreStorage(options.group, function(err, storage) {</td></tr><tr class="hit"><td class="line">124</td><td class="hits">8</td><td class="source">        if (null != err) {</td></tr><tr class="miss"><td class="line">125</td><td class="hits">0</td><td class="source">            callback(err)</td></tr><tr class="miss"><td class="line">126</td><td class="hits">0</td><td class="source">            return</td></tr><tr><td class="line">127</td><td class="hits"></td><td class="source">        }</td></tr><tr class="hit"><td class="line">128</td><td class="hits">8</td><td class="source">        storage.upload(file, options, callback)</td></tr><tr><td class="line">129</td><td class="hits"></td><td class="source">    })</td></tr><tr><td class="line">130</td><td class="hits"></td><td class="source">}</td></tr><tr><td class="line">131</td><td class="hits"></td><td class="source"> </td></tr><tr><td class="line">132</td><td class="hits"></td><td class="source">// ------------- public methods</td></tr><tr><td class="line">133</td><td class="hits"></td><td class="source"> </td></tr><tr><td class="line">134</td><td class="hits"></td><td class="source">/**</td></tr><tr><td class="line">135</td><td class="hits"></td><td class="source"> * 上传文件</td></tr><tr><td class="line">136</td><td class="hits"></td><td class="source"> * @param file absolute file path or Buffer or ReadableStream</td></tr><tr><td class="line">137</td><td class="hits"></td><td class="source"> * @param options</td></tr><tr><td class="line">138</td><td class="hits"></td><td class="source"> *      options.group: 指定要上传的group, 不指定则由tracker server分配</td></tr><tr><td class="line">139</td><td class="hits"></td><td class="source"> *      options.size: file size, file参数为ReadableStream时必须指定</td></tr><tr><td class="line">140</td><td class="hits"></td><td class="source"> *      options.ext: 上传文件的后缀，不指定则获取file参数的后缀，不含(.)</td></tr><tr><td class="line">141</td><td class="hits"></td><td class="source"> * @param callback</td></tr><tr><td class="line">142</td><td class="hits"></td><td class="source"> */</td></tr><tr class="hit"><td class="line">143</td><td class="hits">1</td><td class="source">FdfsClient.prototype.upload = function(file, options, callback) {</td></tr><tr class="hit"><td class="line">144</td><td class="hits">8</td><td class="source">    var self = this</td></tr><tr class="hit"><td class="line">145</td><td class="hits">8</td><td class="source">    if (util.isFunction(options)) {</td></tr><tr class="hit"><td class="line">146</td><td class="hits">6</td><td class="source">        callback = options</td></tr><tr class="hit"><td class="line">147</td><td class="hits">6</td><td class="source">        options = {}</td></tr><tr><td class="line">148</td><td class="hits"></td><td class="source"> </td></tr><tr><td class="line">149</td><td class="hits"></td><td class="source">    } else {</td></tr><tr class="hit"><td class="line">150</td><td class="hits">2</td><td class="source">        if (!options) {</td></tr><tr class="miss"><td class="line">151</td><td class="hits">0</td><td class="source">            options = {}</td></tr><tr><td class="line">152</td><td class="hits"></td><td class="source">        }</td></tr><tr><td class="line">153</td><td class="hits"></td><td class="source">    }</td></tr><tr><td class="line">154</td><td class="hits"></td><td class="source"> </td></tr><tr class="hit"><td class="line">155</td><td class="hits">8</td><td class="source">    _normalizeUploadParams(file, options, function(err) {</td></tr><tr class="hit"><td class="line">156</td><td class="hits">8</td><td class="source">        if (err) {</td></tr><tr class="miss"><td class="line">157</td><td class="hits">0</td><td class="source">            callback(err)</td></tr><tr class="miss"><td class="line">158</td><td class="hits">0</td><td class="source">            return</td></tr><tr><td class="line">159</td><td class="hits"></td><td class="source">        }</td></tr><tr class="hit"><td class="line">160</td><td class="hits">8</td><td class="source">        if (!options.ext) {</td></tr><tr class="miss"><td class="line">161</td><td class="hits">0</td><td class="source">            options.ext = self.defaultExt</td></tr><tr><td class="line">162</td><td class="hits"></td><td class="source">        }</td></tr><tr><td class="line">163</td><td class="hits"></td><td class="source"> </td></tr><tr class="hit"><td class="line">164</td><td class="hits">8</td><td class="source">        self._upload(file, options, callback)</td></tr><tr><td class="line">165</td><td class="hits"></td><td class="source">    })</td></tr><tr><td class="line">166</td><td class="hits"></td><td class="source">}</td></tr><tr><td class="line">167</td><td class="hits"></td><td class="source"> </td></tr><tr><td class="line">168</td><td class="hits"></td><td class="source">/**</td></tr><tr><td class="line">169</td><td class="hits"></td><td class="source"> * 下载文件</td></tr><tr><td class="line">170</td><td class="hits"></td><td class="source"> * @param fileId</td></tr><tr><td class="line">171</td><td class="hits"></td><td class="source"> * @param options options可以直接传options.target</td></tr><tr><td class="line">172</td><td class="hits"></td><td class="source"> *      options.target 下载的文件流将被写入到这里，可以是本地文件名，也可以是WritableStream，如果为空则每次服务器返回数据的时候都会回调callback</td></tr><tr><td class="line">173</td><td class="hits"></td><td class="source"> *      options.offset和options.bytes: 当只想下载文件中的某1片段时指定</td></tr><tr><td class="line">174</td><td class="hits"></td><td class="source"> * @param callback 若未指定options.target，服务器每次数据的返回都会回调，若指定了options.target，则只在结束时回调一次</td></tr><tr><td class="line">175</td><td class="hits"></td><td class="source"> */</td></tr><tr class="hit"><td class="line">176</td><td class="hits">1</td><td class="source">FdfsClient.prototype.download = function(fileId, options, callback) {</td></tr><tr class="hit"><td class="line">177</td><td class="hits">3</td><td class="source">    if (!options || util.isFunction(options)) {</td></tr><tr class="miss"><td class="line">178</td><td class="hits">0</td><td class="source">        callback(new Error('options.target is not specified'))</td></tr><tr class="miss"><td class="line">179</td><td class="hits">0</td><td class="source">        return</td></tr><tr><td class="line">180</td><td class="hits"></td><td class="source">    }</td></tr><tr><td class="line">181</td><td class="hits"></td><td class="source"> </td></tr><tr><td class="line">182</td><td class="hits"></td><td class="source">    // 直接传入target</td></tr><tr class="hit"><td class="line">183</td><td class="hits">3</td><td class="source">    if (!options.target) {</td></tr><tr class="hit"><td class="line">184</td><td class="hits">2</td><td class="source">        var ori = options</td></tr><tr class="hit"><td class="line">185</td><td class="hits">2</td><td class="source">        options = {}</td></tr><tr class="hit"><td class="line">186</td><td class="hits">2</td><td class="source">        options.target = ori</td></tr><tr><td class="line">187</td><td class="hits"></td><td class="source">    }</td></tr><tr><td class="line">188</td><td class="hits"></td><td class="source"> </td></tr><tr class="hit"><td class="line">189</td><td class="hits">3</td><td class="source">    if (!(util.isString(options.target) || is.writableStream(options.target))) {</td></tr><tr class="miss"><td class="line">190</td><td class="hits">0</td><td class="source">        callback(new Error('options.target is invalid, it\'s type must be String or WritableStream'))</td></tr><tr><td class="line">191</td><td class="hits"></td><td class="source">    }</td></tr><tr><td class="line">192</td><td class="hits"></td><td class="source"> </td></tr><tr class="hit"><td class="line">193</td><td class="hits">3</td><td class="source">    if (util.isString(options.target)) {</td></tr><tr class="hit"><td class="line">194</td><td class="hits">3</td><td class="source">        options.target = fs.createWriteStream(options.target)</td></tr><tr><td class="line">195</td><td class="hits"></td><td class="source">    }</td></tr><tr><td class="line">196</td><td class="hits"></td><td class="source"> </td></tr><tr class="hit"><td class="line">197</td><td class="hits">3</td><td class="source">    this._getTracker().getFetchStorage(fileId, function(err, storage) {</td></tr><tr class="hit"><td class="line">198</td><td class="hits">3</td><td class="source">        storage.download(fileId, options, callback)</td></tr><tr><td class="line">199</td><td class="hits"></td><td class="source">    })</td></tr><tr><td class="line">200</td><td class="hits"></td><td class="source">}</td></tr><tr><td class="line">201</td><td class="hits"></td><td class="source"> </td></tr><tr><td class="line">202</td><td class="hits"></td><td class="source">/**</td></tr><tr><td class="line">203</td><td class="hits"></td><td class="source"> * 删除fileId指定的文件</td></tr><tr><td class="line">204</td><td class="hits"></td><td class="source"> * @param fileId</td></tr><tr><td class="line">205</td><td class="hits"></td><td class="source"> * @param callback</td></tr><tr><td class="line">206</td><td class="hits"></td><td class="source"> */</td></tr><tr class="hit"><td class="line">207</td><td class="hits">1</td><td class="source">FdfsClient.prototype.del = function(fileId, callback) {</td></tr><tr class="hit"><td class="line">208</td><td class="hits">7</td><td class="source">    this._getTracker().getUpdateStorage(fileId, function(err, storage) {</td></tr><tr class="hit"><td class="line">209</td><td class="hits">7</td><td class="source">        if (null != err) {</td></tr><tr class="miss"><td class="line">210</td><td class="hits">0</td><td class="source">            callback(err)</td></tr><tr class="miss"><td class="line">211</td><td class="hits">0</td><td class="source">            return</td></tr><tr><td class="line">212</td><td class="hits"></td><td class="source">        }</td></tr><tr class="hit"><td class="line">213</td><td class="hits">7</td><td class="source">        storage.del(fileId, callback)</td></tr><tr><td class="line">214</td><td class="hits"></td><td class="source">    })</td></tr><tr><td class="line">215</td><td class="hits"></td><td class="source">}</td></tr><tr class="hit"><td class="line">216</td><td class="hits">1</td><td class="source">FdfsClient.prototype.remove = FdfsClient.prototype.del</td></tr><tr><td class="line">217</td><td class="hits"></td><td class="source"> </td></tr><tr><td class="line">218</td><td class="hits"></td><td class="source">/**</td></tr><tr><td class="line">219</td><td class="hits"></td><td class="source"> * @param fileId</td></tr><tr><td class="line">220</td><td class="hits"></td><td class="source"> * @param metaData  {key1: value1, key2: value2}</td></tr><tr><td class="line">221</td><td class="hits"></td><td class="source"> * @param flag 'O' for overwrite all old metadata (default)</td></tr><tr><td class="line">222</td><td class="hits"></td><td class="source">                'M' for merge, insert when the meta item not exist, otherwise update it</td></tr><tr><td class="line">223</td><td class="hits"></td><td class="source"> * @param callback</td></tr><tr><td class="line">224</td><td class="hits"></td><td class="source"> */</td></tr><tr class="hit"><td class="line">225</td><td class="hits">1</td><td class="source">FdfsClient.prototype.setMetaData = function(fileId, metaData, flag, callback) {</td></tr><tr class="hit"><td class="line">226</td><td class="hits">5</td><td class="source">    if (util.isFunction(flag)) {</td></tr><tr class="hit"><td class="line">227</td><td class="hits">3</td><td class="source">        callback = flag</td></tr><tr class="hit"><td class="line">228</td><td class="hits">3</td><td class="source">        flag = 'O'</td></tr><tr><td class="line">229</td><td class="hits"></td><td class="source">    }</td></tr><tr><td class="line">230</td><td class="hits"></td><td class="source"> </td></tr><tr class="hit"><td class="line">231</td><td class="hits">5</td><td class="source">    this._getTracker().getUpdateStorage(fileId, function(err, storage) {</td></tr><tr class="hit"><td class="line">232</td><td class="hits">5</td><td class="source">        if (null != err) {</td></tr><tr class="miss"><td class="line">233</td><td class="hits">0</td><td class="source">            callback(err)</td></tr><tr class="miss"><td class="line">234</td><td class="hits">0</td><td class="source">            return</td></tr><tr><td class="line">235</td><td class="hits"></td><td class="source">        }</td></tr><tr class="hit"><td class="line">236</td><td class="hits">5</td><td class="source">        storage.setMetaData(fileId, metaData, flag, callback)</td></tr><tr><td class="line">237</td><td class="hits"></td><td class="source">    })</td></tr><tr><td class="line">238</td><td class="hits"></td><td class="source">}</td></tr><tr><td class="line">239</td><td class="hits"></td><td class="source"> </td></tr><tr><td class="line">240</td><td class="hits"></td><td class="source">/**</td></tr><tr><td class="line">241</td><td class="hits"></td><td class="source"> * 获取指定fileId的meta data</td></tr><tr><td class="line">242</td><td class="hits"></td><td class="source"> * @param fileId</td></tr><tr><td class="line">243</td><td class="hits"></td><td class="source"> * @param callback</td></tr><tr><td class="line">244</td><td class="hits"></td><td class="source"> */</td></tr><tr class="hit"><td class="line">245</td><td class="hits">1</td><td class="source">FdfsClient.prototype.getMetaData = function(fileId, callback) {</td></tr><tr class="hit"><td class="line">246</td><td class="hits">3</td><td class="source">    this._getTracker().getUpdateStorage(fileId, function(err, storage) {</td></tr><tr class="hit"><td class="line">247</td><td class="hits">3</td><td class="source">        if (null != err) {</td></tr><tr class="miss"><td class="line">248</td><td class="hits">0</td><td class="source">            callback(err)</td></tr><tr class="miss"><td class="line">249</td><td class="hits">0</td><td class="source">            return</td></tr><tr><td class="line">250</td><td class="hits"></td><td class="source">        }</td></tr><tr class="hit"><td class="line">251</td><td class="hits">3</td><td class="source">        storage.getMetaData(fileId, callback)</td></tr><tr><td class="line">252</td><td class="hits"></td><td class="source">    })</td></tr><tr><td class="line">253</td><td class="hits"></td><td class="source">}</td></tr><tr><td class="line">254</td><td class="hits"></td><td class="source"> </td></tr><tr><td class="line">255</td><td class="hits"></td><td class="source">/**</td></tr><tr><td class="line">256</td><td class="hits"></td><td class="source"> * 获取指定fileId的信息</td></tr><tr><td class="line">257</td><td class="hits"></td><td class="source"> * fileInfo会传给回调，结构如下</td></tr><tr><td class="line">258</td><td class="hits"></td><td class="source"> *  {</td></tr><tr><td class="line">259</td><td class="hits"></td><td class="source"> *      // 文件大小</td></tr><tr><td class="line">260</td><td class="hits"></td><td class="source"> *      size:</td></tr><tr><td class="line">261</td><td class="hits"></td><td class="source"> *      // 文件创建的UTC时间戳，单位为秒</td></tr><tr><td class="line">262</td><td class="hits"></td><td class="source"> *      timestamp:</td></tr><tr><td class="line">263</td><td class="hits"></td><td class="source"> *      crc32:</td></tr><tr><td class="line">264</td><td class="hits"></td><td class="source"> *      // 最初上传到的storage server的ip</td></tr><tr><td class="line">265</td><td class="hits"></td><td class="source"> *      addr:</td></tr><tr><td class="line">266</td><td class="hits"></td><td class="source"> *  }</td></tr><tr><td class="line">267</td><td class="hits"></td><td class="source"> * @param fileId</td></tr><tr><td class="line">268</td><td class="hits"></td><td class="source"> * @param callback</td></tr><tr><td class="line">269</td><td class="hits"></td><td class="source"> */</td></tr><tr class="hit"><td class="line">270</td><td class="hits">1</td><td class="source">FdfsClient.prototype.getFileInfo = function(fileId, callback) {</td></tr><tr class="hit"><td class="line">271</td><td class="hits">1</td><td class="source">    this._getTracker().getUpdateStorage(fileId, function(err, storage) {</td></tr><tr class="hit"><td class="line">272</td><td class="hits">1</td><td class="source">        if (err) {</td></tr><tr class="miss"><td class="line">273</td><td class="hits">0</td><td class="source">            callback(err)</td></tr><tr class="miss"><td class="line">274</td><td class="hits">0</td><td class="source">            return</td></tr><tr><td class="line">275</td><td class="hits"></td><td class="source">        }</td></tr><tr class="hit"><td class="line">276</td><td class="hits">1</td><td class="source">        storage.getFileInfo(fileId, callback)</td></tr><tr><td class="line">277</td><td class="hits"></td><td class="source">    })</td></tr><tr><td class="line">278</td><td class="hits"></td><td class="source">}</td></tr><tr><td class="line">279</td><td class="hits"></td><td class="source"> </td></tr><tr><td class="line">280</td><td class="hits"></td><td class="source">// -------------- helpers</td></tr><tr><td class="line">281</td><td class="hits"></td><td class="source">/**</td></tr><tr><td class="line">282</td><td class="hits"></td><td class="source"> * 验证file参数是否合法，同时补充一些必要的参数</td></tr><tr><td class="line">283</td><td class="hits"></td><td class="source"> * 若为String，则需验证是否存在</td></tr><tr><td class="line">284</td><td class="hits"></td><td class="source"> * 若为ReadableStream，则需验证options.size是否存在</td></tr><tr><td class="line">285</td><td class="hits"></td><td class="source"> * @param file</td></tr><tr><td class="line">286</td><td class="hits"></td><td class="source"> * @param options</td></tr><tr><td class="line">287</td><td class="hits"></td><td class="source"> * @param callback</td></tr><tr><td class="line">288</td><td class="hits"></td><td class="source"> * @private</td></tr><tr><td class="line">289</td><td class="hits"></td><td class="source"> */</td></tr><tr class="hit"><td class="line">290</td><td class="hits">1</td><td class="source">function _normalizeUploadParams(file, options, callback) {</td></tr><tr class="hit"><td class="line">291</td><td class="hits">8</td><td class="source">    if (!file) {</td></tr><tr class="miss"><td class="line">292</td><td class="hits">0</td><td class="source">        callback(new Error('The &quot;file&quot; parameter is empty.'))</td></tr><tr class="miss"><td class="line">293</td><td class="hits">0</td><td class="source">        return</td></tr><tr><td class="line">294</td><td class="hits"></td><td class="source">    }</td></tr><tr><td class="line">295</td><td class="hits"></td><td class="source"> </td></tr><tr class="hit"><td class="line">296</td><td class="hits">8</td><td class="source">    if (!(util.isString(file) || util.isBuffer(file) || is.readableStream(file))) {</td></tr><tr class="miss"><td class="line">297</td><td class="hits">0</td><td class="source">        callback(new Error('The &quot;file&quot; parameter is invalid, it must be a String, Buffer, or ReadableStream'))</td></tr><tr class="miss"><td class="line">298</td><td class="hits">0</td><td class="source">        return</td></tr><tr><td class="line">299</td><td class="hits"></td><td class="source">    }</td></tr><tr><td class="line">300</td><td class="hits"></td><td class="source"> </td></tr><tr class="hit"><td class="line">301</td><td class="hits">8</td><td class="source">    if (util.isString(file)) {</td></tr><tr class="hit"><td class="line">302</td><td class="hits">8</td><td class="source">        fs.stat(file, function(err, stats) {</td></tr><tr class="hit"><td class="line">303</td><td class="hits">8</td><td class="source">            if (err || !stats) {</td></tr><tr class="miss"><td class="line">304</td><td class="hits">0</td><td class="source">                callback(new Error('File [' + file + '] is not exists!'))</td></tr><tr class="miss"><td class="line">305</td><td class="hits">0</td><td class="source">                return</td></tr><tr><td class="line">306</td><td class="hits"></td><td class="source">            }</td></tr><tr><td class="line">307</td><td class="hits"></td><td class="source"> </td></tr><tr class="hit"><td class="line">308</td><td class="hits">8</td><td class="source">            options.size = stats.size</td></tr><tr class="hit"><td class="line">309</td><td class="hits">8</td><td class="source">            if (!options.ext) {</td></tr><tr class="hit"><td class="line">310</td><td class="hits">7</td><td class="source">                options.ext = path.extname(file)</td></tr><tr class="hit"><td class="line">311</td><td class="hits">7</td><td class="source">                if (options.ext) {</td></tr><tr><td class="line">312</td><td class="hits"></td><td class="source">                    // 去掉.</td></tr><tr class="hit"><td class="line">313</td><td class="hits">7</td><td class="source">                    options.ext = options.ext.substring(1)</td></tr><tr><td class="line">314</td><td class="hits"></td><td class="source">                }</td></tr><tr><td class="line">315</td><td class="hits"></td><td class="source">            }</td></tr><tr class="hit"><td class="line">316</td><td class="hits">8</td><td class="source">            callback(null)</td></tr><tr><td class="line">317</td><td class="hits"></td><td class="source">        })</td></tr><tr class="hit"><td class="line">318</td><td class="hits">8</td><td class="source">        return</td></tr><tr><td class="line">319</td><td class="hits"></td><td class="source">    }</td></tr><tr><td class="line">320</td><td class="hits"></td><td class="source"> </td></tr><tr class="miss"><td class="line">321</td><td class="hits">0</td><td class="source">    if (is.readableStream(file) &amp;&amp; !options.size) {</td></tr><tr class="miss"><td class="line">322</td><td class="hits">0</td><td class="source">        callback(new Error('when the &quot;file&quot; parameter\'s is ReadableStream, options.size must specified'))</td></tr><tr class="miss"><td class="line">323</td><td class="hits">0</td><td class="source">        return</td></tr><tr><td class="line">324</td><td class="hits"></td><td class="source">    }</td></tr><tr><td class="line">325</td><td class="hits"></td><td class="source"> </td></tr><tr class="miss"><td class="line">326</td><td class="hits">0</td><td class="source">    if (util.isBuffer(file)) {</td></tr><tr class="miss"><td class="line">327</td><td class="hits">0</td><td class="source">        options.size = file.length</td></tr><tr><td class="line">328</td><td class="hits"></td><td class="source">    }</td></tr><tr><td class="line">329</td><td class="hits"></td><td class="source"> </td></tr><tr class="miss"><td class="line">330</td><td class="hits">0</td><td class="source">    callback(null)</td></tr><tr><td class="line">331</td><td class="hits"></td><td class="source">}</td></tr><tr><td class="line">332</td><td class="hits"></td><td class="source"> </td></tr><tr><td class="line">333</td><td class="hits"></td><td class="source"> </td></tr><tr><td class="line">334</td><td class="hits"></td><td class="source">// expose</td></tr><tr class="hit"><td class="line">335</td><td class="hits">1</td><td class="source">module.exports = FdfsClient</td></tr></tbody></table></div><div class="file"><h2 id="/Users/chenboxiang/WebstormProjects/fdfs-client/lib/helpers.js">/Users/chenboxiang/WebstormProjects/fdfs-client/lib/helpers.js</h2><div id="stats" class="high"><div class="percentage">93%</div><div class="sloc">30</div><div class="hits">28</div><div class="misses">2</div></div><table id="source"><thead><tr><th>Line</th><th>Hits</th><th>Source</th></tr></thead><tbody><tr><td class="line">1</td><td class="hits"></td><td class="source">/**</td></tr><tr><td class="line">2</td><td class="hits"></td><td class="source"> * Author: chenboxiang</td></tr><tr><td class="line">3</td><td class="hits"></td><td class="source"> * Date: 14-6-13</td></tr><tr><td class="line">4</td><td class="hits"></td><td class="source"> * Time: 下午8:50</td></tr><tr><td class="line">5</td><td class="hits"></td><td class="source"> */</td></tr><tr class="hit"><td class="line">6</td><td class="hits">1</td><td class="source">'use strict'</td></tr><tr><td class="line">7</td><td class="hits"></td><td class="source"> </td></tr><tr class="hit"><td class="line">8</td><td class="hits">1</td><td class="source">var BigNumber = require('bignumber.js')</td></tr><tr class="hit"><td class="line">9</td><td class="hits">1</td><td class="source">var Buffer = require('buffer').Buffer</td></tr><tr class="hit"><td class="line">10</td><td class="hits">1</td><td class="source">var trimReg = /^\u0000+|\u0000+$/g</td></tr><tr><td class="line">11</td><td class="hits"></td><td class="source"> </td></tr><tr class="hit"><td class="line">12</td><td class="hits">1</td><td class="source">module.exports = {</td></tr><tr><td class="line">13</td><td class="hits"></td><td class="source">    /**</td></tr><tr><td class="line">14</td><td class="hits"></td><td class="source">     * 将unsigned number转换为Big-Endian buffer</td></tr><tr><td class="line">15</td><td class="hits"></td><td class="source">     * @param number</td></tr><tr><td class="line">16</td><td class="hits"></td><td class="source">     * @param bytes buffer的字节数</td></tr><tr><td class="line">17</td><td class="hits"></td><td class="source">     */</td></tr><tr><td class="line">18</td><td class="hits"></td><td class="source">    number2Buffer: function(number, bytes) {</td></tr><tr class="hit"><td class="line">19</td><td class="hits">130</td><td class="source">        if (number &lt; 0) {</td></tr><tr class="miss"><td class="line">20</td><td class="hits">0</td><td class="source">            throw new Error('&quot;number&quot; must greater than or equal to zero.')</td></tr><tr><td class="line">21</td><td class="hits"></td><td class="source">        }</td></tr><tr class="hit"><td class="line">22</td><td class="hits">130</td><td class="source">        bytes = bytes || 8</td></tr><tr class="hit"><td class="line">23</td><td class="hits">130</td><td class="source">        var bn = new BigNumber(String(number))</td></tr><tr><td class="line">24</td><td class="hits"></td><td class="source">        // 转换为16进制字符串</td></tr><tr class="hit"><td class="line">25</td><td class="hits">130</td><td class="source">        var hex = bn.toString(16)</td></tr><tr><td class="line">26</td><td class="hits"></td><td class="source"> </td></tr><tr><td class="line">27</td><td class="hits"></td><td class="source">        // ------- 将hex length补充成bytes x 2，不足则高位补0，超过则去掉高位</td></tr><tr class="hit"><td class="line">28</td><td class="hits">130</td><td class="source">        var length = hex.length</td></tr><tr class="hit"><td class="line">29</td><td class="hits">130</td><td class="source">        var targetLength = bytes * 2</td></tr><tr class="hit"><td class="line">30</td><td class="hits">130</td><td class="source">        if (length &lt; targetLength) {</td></tr><tr class="hit"><td class="line">31</td><td class="hits">129</td><td class="source">            var i = targetLength - length</td></tr><tr class="hit"><td class="line">32</td><td class="hits">129</td><td class="source">            while (i &gt; 0) {</td></tr><tr class="hit"><td class="line">33</td><td class="hits">1849</td><td class="source">                hex = '0' + hex</td></tr><tr class="hit"><td class="line">34</td><td class="hits">1849</td><td class="source">                i--</td></tr><tr><td class="line">35</td><td class="hits"></td><td class="source">            }</td></tr><tr><td class="line">36</td><td class="hits"></td><td class="source"> </td></tr><tr class="hit"><td class="line">37</td><td class="hits">1</td><td class="source">        } else if (length &gt; targetLength) {</td></tr><tr class="miss"><td class="line">38</td><td class="hits">0</td><td class="source">            hex = hex.substring(length - targetLength)</td></tr><tr><td class="line">39</td><td class="hits"></td><td class="source">        }</td></tr><tr><td class="line">40</td><td class="hits"></td><td class="source"> </td></tr><tr><td class="line">41</td><td class="hits"></td><td class="source">        // ------ 填充到buffer里，高位在前</td></tr><tr class="hit"><td class="line">42</td><td class="hits">130</td><td class="source">        var buffer = new Buffer(bytes)</td></tr><tr class="hit"><td class="line">43</td><td class="hits">130</td><td class="source">        var offset = 0</td></tr><tr class="hit"><td class="line">44</td><td class="hits">130</td><td class="source">        while (offset &lt; bytes) {</td></tr><tr class="hit"><td class="line">45</td><td class="hits">1036</td><td class="source">            var bn = Number(&quot;0x&quot; + hex.substring(offset * 2, (offset * 2) + 2))</td></tr><tr class="hit"><td class="line">46</td><td class="hits">1036</td><td class="source">            buffer.writeUInt8(bn, offset)</td></tr><tr class="hit"><td class="line">47</td><td class="hits">1036</td><td class="source">            offset++</td></tr><tr><td class="line">48</td><td class="hits"></td><td class="source">        }</td></tr><tr><td class="line">49</td><td class="hits"></td><td class="source"> </td></tr><tr class="hit"><td class="line">50</td><td class="hits">130</td><td class="source">        return buffer</td></tr><tr><td class="line">51</td><td class="hits"></td><td class="source">    },</td></tr><tr><td class="line">52</td><td class="hits"></td><td class="source"> </td></tr><tr><td class="line">53</td><td class="hits"></td><td class="source">    /**</td></tr><tr><td class="line">54</td><td class="hits"></td><td class="source">     * 构造Object.defineProperty中指定的property属性，并达到const声明的效果，只读，不可写</td></tr><tr><td class="line">55</td><td class="hits"></td><td class="source">     * @param value</td></tr><tr><td class="line">56</td><td class="hits"></td><td class="source">     * @returns {{configurable: boolean, writable: boolean, value: *}}</td></tr><tr><td class="line">57</td><td class="hits"></td><td class="source">     */</td></tr><tr><td class="line">58</td><td class="hits"></td><td class="source">    buildConstProp: function(value) {</td></tr><tr class="hit"><td class="line">59</td><td class="hits">64</td><td class="source">        return {</td></tr><tr><td class="line">60</td><td class="hits"></td><td class="source">            configurable: false,</td></tr><tr><td class="line">61</td><td class="hits"></td><td class="source">            writable: false,</td></tr><tr><td class="line">62</td><td class="hits"></td><td class="source">            value: value</td></tr><tr><td class="line">63</td><td class="hits"></td><td class="source">        }</td></tr><tr><td class="line">64</td><td class="hits"></td><td class="source">    },</td></tr><tr><td class="line">65</td><td class="hits"></td><td class="source"> </td></tr><tr><td class="line">66</td><td class="hits"></td><td class="source">    /**</td></tr><tr><td class="line">67</td><td class="hits"></td><td class="source">     * 在String.prototype.trim的基础上再去掉\u0000</td></tr><tr><td class="line">68</td><td class="hits"></td><td class="source">     *  默认trim的处理见下:</td></tr><tr><td class="line">69</td><td class="hits"></td><td class="source">     *      https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/String/trim</td></tr><tr><td class="line">70</td><td class="hits"></td><td class="source">     *      http://blog.stevenlevithan.com/archives/faster-trim-javascript</td></tr><tr><td class="line">71</td><td class="hits"></td><td class="source">     *      http://jsperf.com/mega-trim-test</td></tr><tr><td class="line">72</td><td class="hits"></td><td class="source">     * @param str</td></tr><tr><td class="line">73</td><td class="hits"></td><td class="source">     * @returns {string}</td></tr><tr><td class="line">74</td><td class="hits"></td><td class="source">     */</td></tr><tr><td class="line">75</td><td class="hits"></td><td class="source">    trim: function(str) {</td></tr><tr class="hit"><td class="line">76</td><td class="hits">88</td><td class="source">        return str.trim().replace(trimReg, '')</td></tr><tr><td class="line">77</td><td class="hits"></td><td class="source">    },</td></tr><tr><td class="line">78</td><td class="hits"></td><td class="source"> </td></tr><tr><td class="line">79</td><td class="hits"></td><td class="source">    /**</td></tr><tr><td class="line">80</td><td class="hits"></td><td class="source">     * file id conver to group and filename</td></tr><tr><td class="line">81</td><td class="hits"></td><td class="source">     * @param fileId</td></tr><tr><td class="line">82</td><td class="hits"></td><td class="source">     * @returns {{group: string, filename: string}}</td></tr><tr><td class="line">83</td><td class="hits"></td><td class="source">     */</td></tr><tr><td class="line">84</td><td class="hits"></td><td class="source">    id2gf: function(fileId) {</td></tr><tr class="hit"><td class="line">85</td><td class="hits">39</td><td class="source">        var pos = fileId.indexOf('/')</td></tr><tr class="hit"><td class="line">86</td><td class="hits">39</td><td class="source">        return {</td></tr><tr><td class="line">87</td><td class="hits"></td><td class="source">            group: fileId.substring(0, pos),</td></tr><tr><td class="line">88</td><td class="hits"></td><td class="source">            filename: fileId.substring(pos + 1)</td></tr><tr><td class="line">89</td><td class="hits"></td><td class="source">        }</td></tr><tr><td class="line">90</td><td class="hits"></td><td class="source">    }</td></tr><tr><td class="line">91</td><td class="hits"></td><td class="source">}</td></tr></tbody></table></div><div class="file"><h2 id="/Users/chenboxiang/WebstormProjects/fdfs-client/lib/logger.js">/Users/chenboxiang/WebstormProjects/fdfs-client/lib/logger.js</h2><div id="stats" class="high"><div class="percentage">100%</div><div class="sloc">10</div><div class="hits">10</div><div class="misses">0</div></div><table id="source"><thead><tr><th>Line</th><th>Hits</th><th>Source</th></tr></thead><tbody><tr><td class="line">1</td><td class="hits"></td><td class="source">/**</td></tr><tr><td class="line">2</td><td class="hits"></td><td class="source"> * Author: chenboxiang</td></tr><tr><td class="line">3</td><td class="hits"></td><td class="source"> * Date: 14-6-15</td></tr><tr><td class="line">4</td><td class="hits"></td><td class="source"> * Time: 下午1:59</td></tr><tr><td class="line">5</td><td class="hits"></td><td class="source"> */</td></tr><tr class="hit"><td class="line">6</td><td class="hits">1</td><td class="source">'use strict';</td></tr><tr><td class="line">7</td><td class="hits"></td><td class="source"> </td></tr><tr class="hit"><td class="line">8</td><td class="hits">1</td><td class="source">var _logger = console</td></tr><tr><td class="line">9</td><td class="hits"></td><td class="source"> </td></tr><tr class="hit"><td class="line">10</td><td class="hits">1</td><td class="source">var logger = {</td></tr><tr><td class="line">11</td><td class="hits"></td><td class="source">    setLogger: function(newLogger) {</td></tr><tr class="hit"><td class="line">12</td><td class="hits">1</td><td class="source">        _logger = newLogger</td></tr><tr><td class="line">13</td><td class="hits"></td><td class="source">    }</td></tr><tr><td class="line">14</td><td class="hits"></td><td class="source">}</td></tr><tr><td class="line">15</td><td class="hits"></td><td class="source"> </td></tr><tr class="hit"><td class="line">16</td><td class="hits">1</td><td class="source">var methods = 'log trace debug info warn error fatal'.split(' ')</td></tr><tr class="hit"><td class="line">17</td><td class="hits">1</td><td class="source">methods.forEach(function(method) {</td></tr><tr class="hit"><td class="line">18</td><td class="hits">7</td><td class="source">    logger[method] = function() {</td></tr><tr class="hit"><td class="line">19</td><td class="hits">251</td><td class="source">        var oriLog = _logger[method] || _logger['log']</td></tr><tr class="hit"><td class="line">20</td><td class="hits">251</td><td class="source">        oriLog.apply(_logger, arguments)</td></tr><tr><td class="line">21</td><td class="hits"></td><td class="source">    }</td></tr><tr><td class="line">22</td><td class="hits"></td><td class="source">})</td></tr><tr><td class="line">23</td><td class="hits"></td><td class="source"> </td></tr><tr class="hit"><td class="line">24</td><td class="hits">1</td><td class="source">module.exports = logger</td></tr><tr><td class="line">25</td><td class="hits"></td><td class="source"> </td></tr><tr><td class="line">26</td><td class="hits"></td><td class="source"> </td></tr></tbody></table></div><div class="file"><h2 id="/Users/chenboxiang/WebstormProjects/fdfs-client/lib/protocol.js">/Users/chenboxiang/WebstormProjects/fdfs-client/lib/protocol.js</h2><div id="stats" class="high"><div class="percentage">87%</div><div class="sloc">110</div><div class="hits">96</div><div class="misses">14</div></div><table id="source"><thead><tr><th>Line</th><th>Hits</th><th>Source</th></tr></thead><tbody><tr><td class="line">1</td><td class="hits"></td><td class="source">/**</td></tr><tr><td class="line">2</td><td class="hits"></td><td class="source"> * 协议相关的处理</td></tr><tr><td class="line">3</td><td class="hits"></td><td class="source"> * 协议参照 http://bbs.chinaunix.net/thread-2001015-1-1.html</td></tr><tr><td class="line">4</td><td class="hits"></td><td class="source"> * Author: chenboxiang</td></tr><tr><td class="line">5</td><td class="hits"></td><td class="source"> * Date: 14-6-15</td></tr><tr><td class="line">6</td><td class="hits"></td><td class="source"> * Time: 上午9:36</td></tr><tr><td class="line">7</td><td class="hits"></td><td class="source"> */</td></tr><tr class="hit"><td class="line">8</td><td class="hits">1</td><td class="source">'use strict'</td></tr><tr><td class="line">9</td><td class="hits"></td><td class="source"> </td></tr><tr class="hit"><td class="line">10</td><td class="hits">1</td><td class="source">var helpers = require('./helpers')</td></tr><tr class="hit"><td class="line">11</td><td class="hits">1</td><td class="source">var Buffer = require('buffer').Buffer</td></tr><tr class="hit"><td class="line">12</td><td class="hits">1</td><td class="source">var _ = require('lodash')</td></tr><tr class="hit"><td class="line">13</td><td class="hits">1</td><td class="source">var logger = require('./logger')</td></tr><tr class="hit"><td class="line">14</td><td class="hits">1</td><td class="source">var util = require('util')</td></tr><tr class="hit"><td class="line">15</td><td class="hits">1</td><td class="source">var BigNumber = require('bignumber.js')</td></tr><tr><td class="line">16</td><td class="hits"></td><td class="source"> </td></tr><tr class="hit"><td class="line">17</td><td class="hits">1</td><td class="source">var protocol = {}</td></tr><tr><td class="line">18</td><td class="hits"></td><td class="source"> </td></tr><tr class="hit"><td class="line">19</td><td class="hits">1</td><td class="source">Object.defineProperties(protocol, {</td></tr><tr><td class="line">20</td><td class="hits"></td><td class="source">    FDFS_PROTO_CMD_QUIT: helpers.buildConstProp(82),</td></tr><tr><td class="line">21</td><td class="hits"></td><td class="source">    TRACKER_PROTO_CMD_SERVER_LIST_GROUP: helpers.buildConstProp(91),</td></tr><tr><td class="line">22</td><td class="hits"></td><td class="source">    TRACKER_PROTO_CMD_SERVER_LIST_STORAGE: helpers.buildConstProp(92),</td></tr><tr><td class="line">23</td><td class="hits"></td><td class="source">    TRACKER_PROTO_CMD_SERVER_DELETE_STORAGE: helpers.buildConstProp(93),</td></tr><tr><td class="line">24</td><td class="hits"></td><td class="source"> </td></tr><tr><td class="line">25</td><td class="hits"></td><td class="source">    TRACKER_PROTO_CMD_SERVICE_QUERY_STORE_WITHOUT_GROUP_ONE: helpers.buildConstProp(101),</td></tr><tr><td class="line">26</td><td class="hits"></td><td class="source">    TRACKER_PROTO_CMD_SERVICE_QUERY_FETCH_ONE: helpers.buildConstProp(102),</td></tr><tr><td class="line">27</td><td class="hits"></td><td class="source">    TRACKER_PROTO_CMD_SERVICE_QUERY_UPDATE: helpers.buildConstProp(103),</td></tr><tr><td class="line">28</td><td class="hits"></td><td class="source">    TRACKER_PROTO_CMD_SERVICE_QUERY_STORE_WITH_GROUP_ONE: helpers.buildConstProp(104),</td></tr><tr><td class="line">29</td><td class="hits"></td><td class="source">    TRACKER_PROTO_CMD_SERVICE_QUERY_FETCH_ALL: helpers.buildConstProp(105),</td></tr><tr><td class="line">30</td><td class="hits"></td><td class="source">    TRACKER_PROTO_CMD_SERVICE_QUERY_STORE_WITHOUT_GROUP_ALL: helpers.buildConstProp(106),</td></tr><tr><td class="line">31</td><td class="hits"></td><td class="source">    TRACKER_PROTO_CMD_SERVICE_QUERY_STORE_WITH_GROUP_ALL: helpers.buildConstProp(107),</td></tr><tr><td class="line">32</td><td class="hits"></td><td class="source">    TRACKER_PROTO_CMD_RESP: helpers.buildConstProp(100),</td></tr><tr><td class="line">33</td><td class="hits"></td><td class="source">    FDFS_PROTO_CMD_ACTIVE_TEST: helpers.buildConstProp(111),</td></tr><tr><td class="line">34</td><td class="hits"></td><td class="source">    STORAGE_PROTO_CMD_UPLOAD_FILE: helpers.buildConstProp(11),</td></tr><tr><td class="line">35</td><td class="hits"></td><td class="source">    STORAGE_PROTO_CMD_DELETE_FILE: helpers.buildConstProp(12),</td></tr><tr><td class="line">36</td><td class="hits"></td><td class="source">    STORAGE_PROTO_CMD_SET_METADATA: helpers.buildConstProp(13),</td></tr><tr><td class="line">37</td><td class="hits"></td><td class="source">    STORAGE_PROTO_CMD_DOWNLOAD_FILE: helpers.buildConstProp(14),</td></tr><tr><td class="line">38</td><td class="hits"></td><td class="source">    STORAGE_PROTO_CMD_GET_METADATA: helpers.buildConstProp(15),</td></tr><tr><td class="line">39</td><td class="hits"></td><td class="source">    STORAGE_PROTO_CMD_UPLOAD_SLAVE_FILE: helpers.buildConstProp(21),</td></tr><tr><td class="line">40</td><td class="hits"></td><td class="source">    STORAGE_PROTO_CMD_QUERY_FILE_INFO: helpers.buildConstProp(22),</td></tr><tr><td class="line">41</td><td class="hits"></td><td class="source">    STORAGE_PROTO_CMD_UPLOAD_APPENDER_FILE: helpers.buildConstProp(23),  //create appender file</td></tr><tr><td class="line">42</td><td class="hits"></td><td class="source">    STORAGE_PROTO_CMD_APPEND_FILE: helpers.buildConstProp(24),  //append file</td></tr><tr><td class="line">43</td><td class="hits"></td><td class="source">    STORAGE_PROTO_CMD_MODIFY_FILE: helpers.buildConstProp(34),  //modify appender file</td></tr><tr><td class="line">44</td><td class="hits"></td><td class="source">    STORAGE_PROTO_CMD_TRUNCATE_FILE: helpers.buildConstProp(36),  //truncate appender file</td></tr><tr><td class="line">45</td><td class="hits"></td><td class="source"> </td></tr><tr><td class="line">46</td><td class="hits"></td><td class="source"> </td></tr><tr><td class="line">47</td><td class="hits"></td><td class="source">    FDFS_STORAGE_STATUS_INIT: helpers.buildConstProp(0),</td></tr><tr><td class="line">48</td><td class="hits"></td><td class="source">    FDFS_STORAGE_STATUS_WAIT_SYNC: helpers.buildConstProp(1),</td></tr><tr><td class="line">49</td><td class="hits"></td><td class="source">    FDFS_STORAGE_STATUS_SYNCING: helpers.buildConstProp(2),</td></tr><tr><td class="line">50</td><td class="hits"></td><td class="source">    FDFS_STORAGE_STATUS_IP_CHANGED: helpers.buildConstProp(3),</td></tr><tr><td class="line">51</td><td class="hits"></td><td class="source">    FDFS_STORAGE_STATUS_DELETED: helpers.buildConstProp(4),</td></tr><tr><td class="line">52</td><td class="hits"></td><td class="source">    FDFS_STORAGE_STATUS_OFFLINE: helpers.buildConstProp(5),</td></tr><tr><td class="line">53</td><td class="hits"></td><td class="source">    FDFS_STORAGE_STATUS_ONLINE: helpers.buildConstProp(6),</td></tr><tr><td class="line">54</td><td class="hits"></td><td class="source">    FDFS_STORAGE_STATUS_ACTIVE: helpers.buildConstProp(7),</td></tr><tr><td class="line">55</td><td class="hits"></td><td class="source">    FDFS_STORAGE_STATUS_NONE: helpers.buildConstProp(99),</td></tr><tr><td class="line">56</td><td class="hits"></td><td class="source"> </td></tr><tr><td class="line">57</td><td class="hits"></td><td class="source">    /**</td></tr><tr><td class="line">58</td><td class="hits"></td><td class="source">     * for overwrite all old metadata</td></tr><tr><td class="line">59</td><td class="hits"></td><td class="source">     */</td></tr><tr><td class="line">60</td><td class="hits"></td><td class="source">    STORAGE_SET_METADATA_FLAG_OVERWRITE: helpers.buildConstProp('O'),</td></tr><tr><td class="line">61</td><td class="hits"></td><td class="source"> </td></tr><tr><td class="line">62</td><td class="hits"></td><td class="source">    /**</td></tr><tr><td class="line">63</td><td class="hits"></td><td class="source">     * for replace, insert when the meta item not exist, otherwise update it</td></tr><tr><td class="line">64</td><td class="hits"></td><td class="source">     */</td></tr><tr><td class="line">65</td><td class="hits"></td><td class="source">    STORAGE_SET_METADATA_FLAG_MERGE: helpers.buildConstProp('M'),</td></tr><tr><td class="line">66</td><td class="hits"></td><td class="source"> </td></tr><tr><td class="line">67</td><td class="hits"></td><td class="source">    FDFS_PROTO_PKG_LEN_SIZE: helpers.buildConstProp(8),</td></tr><tr><td class="line">68</td><td class="hits"></td><td class="source">    FDFS_PROTO_CMD_SIZE: helpers.buildConstProp(1),</td></tr><tr><td class="line">69</td><td class="hits"></td><td class="source">    FDFS_GROUP_NAME_MAX_LEN: helpers.buildConstProp(16),</td></tr><tr><td class="line">70</td><td class="hits"></td><td class="source">    FDFS_IPADDR_SIZE: helpers.buildConstProp(16),</td></tr><tr><td class="line">71</td><td class="hits"></td><td class="source">    FDFS_DOMAIN_NAME_MAX_SIZE: helpers.buildConstProp(128),</td></tr><tr><td class="line">72</td><td class="hits"></td><td class="source">    FDFS_VERSION_SIZE: helpers.buildConstProp(6),</td></tr><tr><td class="line">73</td><td class="hits"></td><td class="source">    FDFS_STORAGE_ID_MAX_SIZE: helpers.buildConstProp(16),</td></tr><tr><td class="line">74</td><td class="hits"></td><td class="source"> </td></tr><tr><td class="line">75</td><td class="hits"></td><td class="source">    FDFS_RECORD_SEPERATOR: helpers.buildConstProp('\u0001'),</td></tr><tr><td class="line">76</td><td class="hits"></td><td class="source">    FDFS_FIELD_SEPERATOR: helpers.buildConstProp('\u0002'),</td></tr><tr><td class="line">77</td><td class="hits"></td><td class="source"> </td></tr><tr><td class="line">78</td><td class="hits"></td><td class="source">    FDFS_FILE_EXT_NAME_MAX_LEN: helpers.buildConstProp(6),</td></tr><tr><td class="line">79</td><td class="hits"></td><td class="source">    FDFS_FILE_PREFIX_MAX_LEN: helpers.buildConstProp(16),</td></tr><tr><td class="line">80</td><td class="hits"></td><td class="source">    FDFS_FILE_PATH_LEN: helpers.buildConstProp(10),</td></tr><tr><td class="line">81</td><td class="hits"></td><td class="source">    FDFS_FILENAME_BASE64_LENGTH: helpers.buildConstProp(27),</td></tr><tr><td class="line">82</td><td class="hits"></td><td class="source">    FDFS_TRUNK_FILE_INFO_LEN: helpers.buildConstProp(16),</td></tr><tr><td class="line">83</td><td class="hits"></td><td class="source"> </td></tr><tr><td class="line">84</td><td class="hits"></td><td class="source">    ERR_NO_ENOENT: helpers.buildConstProp(2),</td></tr><tr><td class="line">85</td><td class="hits"></td><td class="source">    ERR_NO_EIO: helpers.buildConstProp(5),</td></tr><tr><td class="line">86</td><td class="hits"></td><td class="source">    ERR_NO_EBUSY: helpers.buildConstProp(16),</td></tr><tr><td class="line">87</td><td class="hits"></td><td class="source">    ERR_NO_EINVAL: helpers.buildConstProp(22),</td></tr><tr><td class="line">88</td><td class="hits"></td><td class="source">    ERR_NO_ENOSPC: helpers.buildConstProp(28),</td></tr><tr><td class="line">89</td><td class="hits"></td><td class="source">    ECONNREFUSED: helpers.buildConstProp(61),</td></tr><tr><td class="line">90</td><td class="hits"></td><td class="source">    ERR_NO_EALREADY: helpers.buildConstProp(114),</td></tr><tr><td class="line">91</td><td class="hits"></td><td class="source"> </td></tr><tr><td class="line">92</td><td class="hits"></td><td class="source">    // 成功的STATUS</td></tr><tr><td class="line">93</td><td class="hits"></td><td class="source">    HEADER_STATUS_SUCCESS: helpers.buildConstProp(0)</td></tr><tr><td class="line">94</td><td class="hits"></td><td class="source">})</td></tr><tr><td class="line">95</td><td class="hits"></td><td class="source"> </td></tr><tr class="hit"><td class="line">96</td><td class="hits">1</td><td class="source">Object.defineProperties(protocol, {</td></tr><tr><td class="line">97</td><td class="hits"></td><td class="source">    STORAGE_PROTO_CMD_RESP: helpers.buildConstProp(protocol.TRACKER_PROTO_CMD_RESP),</td></tr><tr><td class="line">98</td><td class="hits"></td><td class="source"> </td></tr><tr><td class="line">99</td><td class="hits"></td><td class="source">    TRACKER_QUERY_STORAGE_FETCH_BODY_LEN: helpers.buildConstProp(protocol.FDFS_GROUP_NAME_MAX_LEN + protocol.FDFS_IPADDR_SIZE - 1 + protocol.FDFS_PROTO_PKG_LEN_SIZE),</td></tr><tr><td class="line">100</td><td class="hits"></td><td class="source">    TRACKER_QUERY_STORAGE_STORE_BODY_LEN: helpers.buildConstProp(protocol.FDFS_GROUP_NAME_MAX_LEN + protocol.FDFS_IPADDR_SIZE + protocol.FDFS_PROTO_PKG_LEN_SIZE),</td></tr><tr><td class="line">101</td><td class="hits"></td><td class="source"> </td></tr><tr><td class="line">102</td><td class="hits"></td><td class="source">    PROTO_HEADER_CMD_INDEX: helpers.buildConstProp(protocol.FDFS_PROTO_PKG_LEN_SIZE),</td></tr><tr><td class="line">103</td><td class="hits"></td><td class="source">    PROTO_HEADER_STATUS_INDEX: helpers.buildConstProp(protocol.FDFS_PROTO_PKG_LEN_SIZE + 1),</td></tr><tr><td class="line">104</td><td class="hits"></td><td class="source"> </td></tr><tr><td class="line">105</td><td class="hits"></td><td class="source">    HEADER_BYTE_LENGTH: helpers.buildConstProp(protocol.FDFS_PROTO_PKG_LEN_SIZE + 2)</td></tr><tr><td class="line">106</td><td class="hits"></td><td class="source">})</td></tr><tr><td class="line">107</td><td class="hits"></td><td class="source"> </td></tr><tr><td class="line">108</td><td class="hits"></td><td class="source">// 协议相关的封装方法</td></tr><tr class="hit"><td class="line">109</td><td class="hits">1</td><td class="source">_.extend(protocol, {</td></tr><tr><td class="line">110</td><td class="hits"></td><td class="source">    /**</td></tr><tr><td class="line">111</td><td class="hits"></td><td class="source">     * 封装协议头</td></tr><tr><td class="line">112</td><td class="hits"></td><td class="source">     * @param command</td></tr><tr><td class="line">113</td><td class="hits"></td><td class="source">     * @param bodyLength</td></tr><tr><td class="line">114</td><td class="hits"></td><td class="source">     * @param status</td></tr><tr><td class="line">115</td><td class="hits"></td><td class="source">     * @return {Buffer}</td></tr><tr><td class="line">116</td><td class="hits"></td><td class="source">     */</td></tr><tr><td class="line">117</td><td class="hits"></td><td class="source">    packHeader: function(command, bodyLength, status) {</td></tr><tr class="hit"><td class="line">118</td><td class="hits">109</td><td class="source">        if (null == bodyLength) {</td></tr><tr class="miss"><td class="line">119</td><td class="hits">0</td><td class="source">            bodyLength = 0</td></tr><tr><td class="line">120</td><td class="hits"></td><td class="source">        }</td></tr><tr><td class="line">121</td><td class="hits"></td><td class="source"> </td></tr><tr class="hit"><td class="line">122</td><td class="hits">109</td><td class="source">        if (null == status) {</td></tr><tr class="miss"><td class="line">123</td><td class="hits">0</td><td class="source">            status = 0</td></tr><tr><td class="line">124</td><td class="hits"></td><td class="source">        }</td></tr><tr><td class="line">125</td><td class="hits"></td><td class="source"> </td></tr><tr><td class="line">126</td><td class="hits"></td><td class="source">        // ----------- 存放1字节的command和1字节的status</td></tr><tr class="hit"><td class="line">127</td><td class="hits">109</td><td class="source">        var buffer = new Buffer(2)</td></tr><tr class="hit"><td class="line">128</td><td class="hits">109</td><td class="source">        buffer.writeUInt8(command, 0)</td></tr><tr class="hit"><td class="line">129</td><td class="hits">109</td><td class="source">        buffer.writeUInt8(status, 1)</td></tr><tr><td class="line">130</td><td class="hits"></td><td class="source"> </td></tr><tr><td class="line">131</td><td class="hits"></td><td class="source">        // 生成8bytes存放body length的buffer</td></tr><tr class="hit"><td class="line">132</td><td class="hits">109</td><td class="source">        var blBuffer = helpers.number2Buffer(bodyLength, 8)</td></tr><tr><td class="line">133</td><td class="hits"></td><td class="source"> </td></tr><tr><td class="line">134</td><td class="hits"></td><td class="source">        // 拼接</td></tr><tr class="hit"><td class="line">135</td><td class="hits">109</td><td class="source">        return Buffer.concat([blBuffer, buffer])</td></tr><tr><td class="line">136</td><td class="hits"></td><td class="source">    },</td></tr><tr><td class="line">137</td><td class="hits"></td><td class="source"> </td></tr><tr><td class="line">138</td><td class="hits"></td><td class="source">    /**</td></tr><tr><td class="line">139</td><td class="hits"></td><td class="source">     * 解析返回的包</td></tr><tr><td class="line">140</td><td class="hits"></td><td class="source">     * @param socket</td></tr><tr><td class="line">141</td><td class="hits"></td><td class="source">     * @param expectedCommand</td></tr><tr><td class="line">142</td><td class="hits"></td><td class="source">     * @param expectedBodyLength</td></tr><tr><td class="line">143</td><td class="hits"></td><td class="source">     * @param callback</td></tr><tr><td class="line">144</td><td class="hits"></td><td class="source">     * @param headerOnly 是否指parse header，下载文件时，解析完header后需将data传递给callback，而非将body接收完</td></tr><tr><td class="line">145</td><td class="hits"></td><td class="source">     */</td></tr><tr><td class="line">146</td><td class="hits"></td><td class="source">    recvPacket: function(socket, expectedCommand, expectedBodyLength, callback, headerOnly) {</td></tr><tr class="hit"><td class="line">147</td><td class="hits">54</td><td class="source">        var oriCallback = callback</td></tr><tr><td class="line">148</td><td class="hits"></td><td class="source">        // 收包完毕则关闭掉连接</td></tr><tr class="hit"><td class="line">149</td><td class="hits">54</td><td class="source">        callback = function() {</td></tr><tr class="hit"><td class="line">150</td><td class="hits">52</td><td class="source">            cleanup()</td></tr><tr class="hit"><td class="line">151</td><td class="hits">52</td><td class="source">            if (util.isFunction(callback)) {</td></tr><tr class="hit"><td class="line">152</td><td class="hits">52</td><td class="source">                oriCallback.apply(null, arguments)</td></tr><tr><td class="line">153</td><td class="hits"></td><td class="source">            }</td></tr><tr><td class="line">154</td><td class="hits"></td><td class="source">        }</td></tr><tr class="hit"><td class="line">155</td><td class="hits">54</td><td class="source">        var headerBufferLen = protocol.HEADER_BYTE_LENGTH</td></tr><tr class="hit"><td class="line">156</td><td class="hits">54</td><td class="source">        var headerBuffer = new Buffer(headerBufferLen)</td></tr><tr><td class="line">157</td><td class="hits"></td><td class="source">        // 已填充的length</td></tr><tr class="hit"><td class="line">158</td><td class="hits">54</td><td class="source">        var headerBufferFilled = 0</td></tr><tr><td class="line">159</td><td class="hits"></td><td class="source">        // 解析后的header信息</td></tr><tr><td class="line">160</td><td class="hits"></td><td class="source">        // {status: , bodyLength: }</td></tr><tr class="hit"><td class="line">161</td><td class="hits">54</td><td class="source">        var header</td></tr><tr class="hit"><td class="line">162</td><td class="hits">54</td><td class="source">        var bodyBuffer</td></tr><tr><td class="line">163</td><td class="hits"></td><td class="source">        // 下1次要copy到bodyBuffer中的起始位置</td></tr><tr class="hit"><td class="line">164</td><td class="hits">54</td><td class="source">        var bodyBufferStart = 0</td></tr><tr><td class="line">165</td><td class="hits"></td><td class="source"> </td></tr><tr class="hit"><td class="line">166</td><td class="hits">54</td><td class="source">        socket.on('data', listener)</td></tr><tr><td class="line">167</td><td class="hits"></td><td class="source"> </td></tr><tr class="hit"><td class="line">168</td><td class="hits">54</td><td class="source">        function listener(data) {</td></tr><tr><td class="line">169</td><td class="hits"></td><td class="source">            // --------------- 收到服务器发来消息</td></tr><tr><td class="line">170</td><td class="hits"></td><td class="source">            // -------- 解析header</td></tr><tr class="hit"><td class="line">171</td><td class="hits">54</td><td class="source">            if (!header) {</td></tr><tr><td class="line">172</td><td class="hits"></td><td class="source">                // header parsed</td></tr><tr class="hit"><td class="line">173</td><td class="hits">54</td><td class="source">                if (headerBufferFilled + data.length &gt;= headerBufferLen) {</td></tr><tr class="hit"><td class="line">174</td><td class="hits">54</td><td class="source">                    var len = headerBufferFilled + data.length</td></tr><tr><td class="line">175</td><td class="hits"></td><td class="source">                    // 只copy剩下的header部分</td></tr><tr class="hit"><td class="line">176</td><td class="hits">54</td><td class="source">                    data.copy(headerBuffer, headerBufferFilled, 0, headerBufferLen - headerBufferFilled)</td></tr><tr class="hit"><td class="line">177</td><td class="hits">54</td><td class="source">                    try {</td></tr><tr class="hit"><td class="line">178</td><td class="hits">54</td><td class="source">                        header = _parseHeader(headerBuffer, expectedCommand, expectedBodyLength)</td></tr><tr class="hit"><td class="line">179</td><td class="hits">53</td><td class="source">                        logger.debug('receive server packet header: %j', header)</td></tr><tr><td class="line">180</td><td class="hits"></td><td class="source">                        // 无body时直接返回</td></tr><tr class="hit"><td class="line">181</td><td class="hits">53</td><td class="source">                        if (header.bodyLength === 0) {</td></tr><tr class="hit"><td class="line">182</td><td class="hits">12</td><td class="source">                            callback(null, header)</td></tr><tr class="hit"><td class="line">183</td><td class="hits">12</td><td class="source">                            return</td></tr><tr><td class="line">184</td><td class="hits"></td><td class="source">                        }</td></tr><tr><td class="line">185</td><td class="hits"></td><td class="source"> </td></tr><tr class="hit"><td class="line">186</td><td class="hits">41</td><td class="source">                        if (headerOnly) {</td></tr><tr class="hit"><td class="line">187</td><td class="hits">2</td><td class="source">                            oriCallback(null, header)</td></tr><tr class="hit"><td class="line">188</td><td class="hits">2</td><td class="source">                            if (len &gt; headerBufferLen) {</td></tr><tr class="hit"><td class="line">189</td><td class="hits">2</td><td class="source">                                oriCallback(null, data.slice(headerBufferLen - headerBufferFilled))</td></tr><tr><td class="line">190</td><td class="hits"></td><td class="source">                            }</td></tr><tr class="hit"><td class="line">191</td><td class="hits">2</td><td class="source">                            return</td></tr><tr><td class="line">192</td><td class="hits"></td><td class="source">                        }</td></tr><tr><td class="line">193</td><td class="hits"></td><td class="source"> </td></tr><tr class="hit"><td class="line">194</td><td class="hits">39</td><td class="source">                        bodyBuffer = new Buffer(header.bodyLength)</td></tr><tr><td class="line">195</td><td class="hits"></td><td class="source"> </td></tr><tr><td class="line">196</td><td class="hits"></td><td class="source">                        // 还有body的数据则填充到body buffer中</td></tr><tr class="hit"><td class="line">197</td><td class="hits">39</td><td class="source">                        if (len &gt; headerBufferLen) {</td></tr><tr class="hit"><td class="line">198</td><td class="hits">39</td><td class="source">                            data.copy(bodyBuffer, 0, headerBufferLen - headerBufferFilled)</td></tr><tr class="hit"><td class="line">199</td><td class="hits">39</td><td class="source">                            bodyBufferStart = len - headerBufferLen</td></tr><tr><td class="line">200</td><td class="hits"></td><td class="source">                            // 读取完毕</td></tr><tr class="hit"><td class="line">201</td><td class="hits">39</td><td class="source">                            if (bodyBufferStart &gt;= header.bodyLength) {</td></tr><tr class="hit"><td class="line">202</td><td class="hits">39</td><td class="source">                                callback(null, bodyBuffer)</td></tr><tr><td class="line">203</td><td class="hits"></td><td class="source">                            }</td></tr><tr><td class="line">204</td><td class="hits"></td><td class="source">                        }</td></tr><tr><td class="line">205</td><td class="hits"></td><td class="source"> </td></tr><tr><td class="line">206</td><td class="hits"></td><td class="source">                    } catch (err) {</td></tr><tr class="hit"><td class="line">207</td><td class="hits">1</td><td class="source">                        callback(err)</td></tr><tr><td class="line">208</td><td class="hits"></td><td class="source">                    }</td></tr><tr><td class="line">209</td><td class="hits"></td><td class="source"> </td></tr><tr><td class="line">210</td><td class="hits"></td><td class="source">                } else {</td></tr><tr class="miss"><td class="line">211</td><td class="hits">0</td><td class="source">                    data.copy(headerBuffer, headerBufferFilled)</td></tr><tr class="miss"><td class="line">212</td><td class="hits">0</td><td class="source">                    headerBufferFilled += data.length</td></tr><tr><td class="line">213</td><td class="hits"></td><td class="source">                }</td></tr><tr><td class="line">214</td><td class="hits"></td><td class="source"> </td></tr><tr><td class="line">215</td><td class="hits"></td><td class="source">            } else {</td></tr><tr><td class="line">216</td><td class="hits"></td><td class="source">                // 交由外部处理</td></tr><tr class="miss"><td class="line">217</td><td class="hits">0</td><td class="source">                if (headerOnly) {</td></tr><tr class="miss"><td class="line">218</td><td class="hits">0</td><td class="source">                    oriCallback(null, data)</td></tr><tr class="miss"><td class="line">219</td><td class="hits">0</td><td class="source">                    return</td></tr><tr><td class="line">220</td><td class="hits"></td><td class="source">                }</td></tr><tr><td class="line">221</td><td class="hits"></td><td class="source">                // ---------- 解析body</td></tr><tr class="miss"><td class="line">222</td><td class="hits">0</td><td class="source">                data.copy(bodyBuffer, bodyBufferStart)</td></tr><tr class="miss"><td class="line">223</td><td class="hits">0</td><td class="source">                bodyBufferStart += data.length</td></tr><tr><td class="line">224</td><td class="hits"></td><td class="source">                // 读取完毕</td></tr><tr class="miss"><td class="line">225</td><td class="hits">0</td><td class="source">                if (bodyBufferStart &gt;= header.bodyLength) {</td></tr><tr class="miss"><td class="line">226</td><td class="hits">0</td><td class="source">                    callback(null, bodyBuffer)</td></tr><tr><td class="line">227</td><td class="hits"></td><td class="source">                }</td></tr><tr><td class="line">228</td><td class="hits"></td><td class="source">            }</td></tr><tr><td class="line">229</td><td class="hits"></td><td class="source">        }</td></tr><tr><td class="line">230</td><td class="hits"></td><td class="source"> </td></tr><tr class="hit"><td class="line">231</td><td class="hits">54</td><td class="source">        function cleanup() {</td></tr><tr class="hit"><td class="line">232</td><td class="hits">52</td><td class="source">            socket.removeListener('data', listener)</td></tr><tr><td class="line">233</td><td class="hits"></td><td class="source">            // 关闭连接</td></tr><tr class="hit"><td class="line">234</td><td class="hits">52</td><td class="source">            protocol.closeSocket(socket)</td></tr><tr><td class="line">235</td><td class="hits"></td><td class="source">        }</td></tr><tr><td class="line">236</td><td class="hits"></td><td class="source">    },</td></tr><tr><td class="line">237</td><td class="hits"></td><td class="source"> </td></tr><tr><td class="line">238</td><td class="hits"></td><td class="source">    /**</td></tr><tr><td class="line">239</td><td class="hits"></td><td class="source">     * 给服务器发送关闭指令，同时end socket</td></tr><tr><td class="line">240</td><td class="hits"></td><td class="source">     * @param socket</td></tr><tr><td class="line">241</td><td class="hits"></td><td class="source">     */</td></tr><tr><td class="line">242</td><td class="hits"></td><td class="source">    closeSocket: function(socket) {</td></tr><tr class="hit"><td class="line">243</td><td class="hits">54</td><td class="source">        socket.end(protocol.packHeader(protocol.FDFS_PROTO_CMD_QUIT, 0, 0))</td></tr><tr><td class="line">244</td><td class="hits"></td><td class="source">    },</td></tr><tr><td class="line">245</td><td class="hits"></td><td class="source"> </td></tr><tr><td class="line">246</td><td class="hits"></td><td class="source">    /**</td></tr><tr><td class="line">247</td><td class="hits"></td><td class="source">     * 封装只有fileId的包，下载和删除文件时使用</td></tr><tr><td class="line">248</td><td class="hits"></td><td class="source">     * @param command</td></tr><tr><td class="line">249</td><td class="hits"></td><td class="source">     * @param group</td></tr><tr><td class="line">250</td><td class="hits"></td><td class="source">     * @param filename</td></tr><tr><td class="line">251</td><td class="hits"></td><td class="source">     * @param charset</td></tr><tr><td class="line">252</td><td class="hits"></td><td class="source">     */</td></tr><tr><td class="line">253</td><td class="hits"></td><td class="source">    packFileId: function(command, group, filename, charset) {</td></tr><tr><td class="line">254</td><td class="hits"></td><td class="source">        // --------- 封装header</td></tr><tr class="hit"><td class="line">255</td><td class="hits">30</td><td class="source">        var fnLength = Buffer.byteLength(filename, charset)</td></tr><tr class="hit"><td class="line">256</td><td class="hits">30</td><td class="source">        var bodyLength = protocol.FDFS_GROUP_NAME_MAX_LEN + fnLength</td></tr><tr class="hit"><td class="line">257</td><td class="hits">30</td><td class="source">        var header = protocol.packHeader(command, bodyLength, 0)</td></tr><tr><td class="line">258</td><td class="hits"></td><td class="source"> </td></tr><tr><td class="line">259</td><td class="hits"></td><td class="source">        // --------- 封装body</td></tr><tr class="hit"><td class="line">260</td><td class="hits">30</td><td class="source">        var body = new Buffer(bodyLength)</td></tr><tr><td class="line">261</td><td class="hits"></td><td class="source">        // 默认都填充上0</td></tr><tr class="hit"><td class="line">262</td><td class="hits">30</td><td class="source">        body.fill(0)</td></tr><tr class="hit"><td class="line">263</td><td class="hits">30</td><td class="source">        var groupBL = Buffer.byteLength(group, charset)</td></tr><tr class="hit"><td class="line">264</td><td class="hits">30</td><td class="source">        body.write(group, 0, groupBL, charset)</td></tr><tr class="hit"><td class="line">265</td><td class="hits">30</td><td class="source">        body.write(filename, protocol.FDFS_GROUP_NAME_MAX_LEN, fnLength, charset)</td></tr><tr><td class="line">266</td><td class="hits"></td><td class="source"> </td></tr><tr class="hit"><td class="line">267</td><td class="hits">30</td><td class="source">        return Buffer.concat([header, body])</td></tr><tr><td class="line">268</td><td class="hits"></td><td class="source">    },</td></tr><tr><td class="line">269</td><td class="hits"></td><td class="source"> </td></tr><tr><td class="line">270</td><td class="hits"></td><td class="source">    /**</td></tr><tr><td class="line">271</td><td class="hits"></td><td class="source">     * 封装meta data</td></tr><tr><td class="line">272</td><td class="hits"></td><td class="source">     * 注：返回的是string，由外部写入到buffer中</td></tr><tr><td class="line">273</td><td class="hits"></td><td class="source">     * @param {Object} metaData</td></tr><tr><td class="line">274</td><td class="hits"></td><td class="source">     * @return {String}</td></tr><tr><td class="line">275</td><td class="hits"></td><td class="source">     */</td></tr><tr><td class="line">276</td><td class="hits"></td><td class="source">    packMetaData: function(metaData) {</td></tr><tr class="hit"><td class="line">277</td><td class="hits">6</td><td class="source">        var first = true</td></tr><tr class="hit"><td class="line">278</td><td class="hits">6</td><td class="source">        var result = ''</td></tr><tr><td class="line">279</td><td class="hits"></td><td class="source"> </td></tr><tr class="hit"><td class="line">280</td><td class="hits">6</td><td class="source">        Object.keys(metaData).forEach(function(key) {</td></tr><tr class="hit"><td class="line">281</td><td class="hits">10</td><td class="source">            if (!first) {</td></tr><tr class="hit"><td class="line">282</td><td class="hits">4</td><td class="source">                result += protocol.FDFS_RECORD_SEPERATOR</td></tr><tr><td class="line">283</td><td class="hits"></td><td class="source"> </td></tr><tr><td class="line">284</td><td class="hits"></td><td class="source">            } else {</td></tr><tr class="hit"><td class="line">285</td><td class="hits">6</td><td class="source">                first = false</td></tr><tr><td class="line">286</td><td class="hits"></td><td class="source">            }</td></tr><tr class="hit"><td class="line">287</td><td class="hits">10</td><td class="source">            var value = metaData[key]</td></tr><tr class="hit"><td class="line">288</td><td class="hits">10</td><td class="source">            result += key</td></tr><tr class="hit"><td class="line">289</td><td class="hits">10</td><td class="source">            result += protocol.FDFS_FIELD_SEPERATOR</td></tr><tr class="hit"><td class="line">290</td><td class="hits">10</td><td class="source">            result += value</td></tr><tr><td class="line">291</td><td class="hits"></td><td class="source">        })</td></tr><tr><td class="line">292</td><td class="hits"></td><td class="source"> </td></tr><tr class="hit"><td class="line">293</td><td class="hits">6</td><td class="source">        return result</td></tr><tr><td class="line">294</td><td class="hits"></td><td class="source">    },</td></tr><tr><td class="line">295</td><td class="hits"></td><td class="source"> </td></tr><tr><td class="line">296</td><td class="hits"></td><td class="source">    /**</td></tr><tr><td class="line">297</td><td class="hits"></td><td class="source">     * raw meta data to structure</td></tr><tr><td class="line">298</td><td class="hits"></td><td class="source">     * @param raw</td></tr><tr><td class="line">299</td><td class="hits"></td><td class="source">     */</td></tr><tr><td class="line">300</td><td class="hits"></td><td class="source">    parseMetaData: function(raw) {</td></tr><tr class="hit"><td class="line">301</td><td class="hits">4</td><td class="source">        var result = {}</td></tr><tr class="hit"><td class="line">302</td><td class="hits">4</td><td class="source">        var md = raw.split(protocol.FDFS_RECORD_SEPERATOR)</td></tr><tr class="hit"><td class="line">303</td><td class="hits">4</td><td class="source">        md.forEach(function(item) {</td></tr><tr class="hit"><td class="line">304</td><td class="hits">8</td><td class="source">            var arr = item.split(protocol.FDFS_FIELD_SEPERATOR)</td></tr><tr class="hit"><td class="line">305</td><td class="hits">8</td><td class="source">            var key = helpers.trim(arr[0])</td></tr><tr class="hit"><td class="line">306</td><td class="hits">8</td><td class="source">            var value = helpers.trim(arr[1])</td></tr><tr class="hit"><td class="line">307</td><td class="hits">8</td><td class="source">            result[key] = value</td></tr><tr><td class="line">308</td><td class="hits"></td><td class="source">        })</td></tr><tr><td class="line">309</td><td class="hits"></td><td class="source"> </td></tr><tr class="hit"><td class="line">310</td><td class="hits">4</td><td class="source">        return result</td></tr><tr><td class="line">311</td><td class="hits"></td><td class="source">    }</td></tr><tr><td class="line">312</td><td class="hits"></td><td class="source">})</td></tr><tr><td class="line">313</td><td class="hits"></td><td class="source"> </td></tr><tr class="hit"><td class="line">314</td><td class="hits">1</td><td class="source">function _parseHeader(headerBuffer, expectedCommand, expectedBodyLength) {</td></tr><tr><td class="line">315</td><td class="hits"></td><td class="source">    // validate buffer length</td></tr><tr class="hit"><td class="line">316</td><td class="hits">54</td><td class="source">    if (headerBuffer.length !== protocol.FDFS_PROTO_PKG_LEN_SIZE + 2) {</td></tr><tr class="miss"><td class="line">317</td><td class="hits">0</td><td class="source">        throw new Error('receive packet size ' + headerBuffer.length + ' is not equal to the expected header size: ' + protocol.FDFS_PROTO_PKG_LEN_SIZE + 2)</td></tr><tr><td class="line">318</td><td class="hits"></td><td class="source">    }</td></tr><tr><td class="line">319</td><td class="hits"></td><td class="source"> </td></tr><tr><td class="line">320</td><td class="hits"></td><td class="source">    // validate command</td></tr><tr class="hit"><td class="line">321</td><td class="hits">54</td><td class="source">    var command = Number('0x' + headerBuffer.toString('hex', protocol.PROTO_HEADER_CMD_INDEX, protocol.PROTO_HEADER_CMD_INDEX + 1))</td></tr><tr class="hit"><td class="line">322</td><td class="hits">54</td><td class="source">    if (expectedCommand !== command) {</td></tr><tr class="miss"><td class="line">323</td><td class="hits">0</td><td class="source">        throw new Error('receive command: ' + command + ' is not equal to the expected command: ' + expectedCommand)</td></tr><tr><td class="line">324</td><td class="hits"></td><td class="source">    }</td></tr><tr><td class="line">325</td><td class="hits"></td><td class="source"> </td></tr><tr><td class="line">326</td><td class="hits"></td><td class="source">    // 应用层错误</td></tr><tr class="hit"><td class="line">327</td><td class="hits">54</td><td class="source">    var status = Number('0x' + headerBuffer.toString('hex', protocol.PROTO_HEADER_STATUS_INDEX, protocol.PROTO_HEADER_STATUS_INDEX + 1))</td></tr><tr class="hit"><td class="line">328</td><td class="hits">54</td><td class="source">    if (status !== protocol.HEADER_STATUS_SUCCESS) {</td></tr><tr class="hit"><td class="line">329</td><td class="hits">1</td><td class="source">        throw new Error('receive packet errno is: ' + status)</td></tr><tr><td class="line">330</td><td class="hits"></td><td class="source">    }</td></tr><tr><td class="line">331</td><td class="hits"></td><td class="source"> </td></tr><tr><td class="line">332</td><td class="hits"></td><td class="source">    // validate body length</td></tr><tr class="hit"><td class="line">333</td><td class="hits">53</td><td class="source">    var bodyLength = new BigNumber(headerBuffer.toString('hex', 0, protocol.FDFS_PROTO_PKG_LEN_SIZE), 16).toNumber()</td></tr><tr class="hit"><td class="line">334</td><td class="hits">53</td><td class="source">    if (null != expectedBodyLength &amp;&amp; expectedBodyLength !== bodyLength) {</td></tr><tr class="miss"><td class="line">335</td><td class="hits">0</td><td class="source">        throw new Error('receive packet body length: ' + bodyLength + ' is not equal to the expected: ' + expectedBodyLength)</td></tr><tr><td class="line">336</td><td class="hits"></td><td class="source">    }</td></tr><tr><td class="line">337</td><td class="hits"></td><td class="source"> </td></tr><tr class="hit"><td class="line">338</td><td class="hits">53</td><td class="source">    return {</td></tr><tr><td class="line">339</td><td class="hits"></td><td class="source">        status: protocol.HEADER_STATUS_SUCCESS,</td></tr><tr><td class="line">340</td><td class="hits"></td><td class="source">        bodyLength: bodyLength</td></tr><tr><td class="line">341</td><td class="hits"></td><td class="source">    }</td></tr><tr><td class="line">342</td><td class="hits"></td><td class="source">}</td></tr><tr><td class="line">343</td><td class="hits"></td><td class="source"> </td></tr><tr class="hit"><td class="line">344</td><td class="hits">1</td><td class="source">module.exports = protocol</td></tr><tr><td class="line">345</td><td class="hits"></td><td class="source"> </td></tr></tbody></table></div><div class="file"><h2 id="/Users/chenboxiang/WebstormProjects/fdfs-client/lib/storage.js">/Users/chenboxiang/WebstormProjects/fdfs-client/lib/storage.js</h2><div id="stats" class="high"><div class="percentage">91%</div><div class="sloc">167</div><div class="hits">153</div><div class="misses">14</div></div><table id="source"><thead><tr><th>Line</th><th>Hits</th><th>Source</th></tr></thead><tbody><tr><td class="line">1</td><td class="hits"></td><td class="source">/**</td></tr><tr><td class="line">2</td><td class="hits"></td><td class="source"> * Author: chenboxiang</td></tr><tr><td class="line">3</td><td class="hits"></td><td class="source"> * Date: 14-6-13</td></tr><tr><td class="line">4</td><td class="hits"></td><td class="source"> * Time: 下午10:18</td></tr><tr><td class="line">5</td><td class="hits"></td><td class="source"> */</td></tr><tr class="hit"><td class="line">6</td><td class="hits">1</td><td class="source">'use strict';</td></tr><tr><td class="line">7</td><td class="hits"></td><td class="source"> </td></tr><tr class="hit"><td class="line">8</td><td class="hits">1</td><td class="source">var EventEmitter = require('events').EventEmitter</td></tr><tr class="hit"><td class="line">9</td><td class="hits">1</td><td class="source">var util = require('util')</td></tr><tr class="hit"><td class="line">10</td><td class="hits">1</td><td class="source">var net = require('net')</td></tr><tr class="hit"><td class="line">11</td><td class="hits">1</td><td class="source">var logger = require('./logger')</td></tr><tr class="hit"><td class="line">12</td><td class="hits">1</td><td class="source">var protocol = require('./protocol')</td></tr><tr class="hit"><td class="line">13</td><td class="hits">1</td><td class="source">var fs = require('fs')</td></tr><tr class="hit"><td class="line">14</td><td class="hits">1</td><td class="source">var helpers = require('./helpers')</td></tr><tr class="hit"><td class="line">15</td><td class="hits">1</td><td class="source">var BigNumber = require('bignumber.js')</td></tr><tr><td class="line">16</td><td class="hits"></td><td class="source"> </td></tr><tr><td class="line">17</td><td class="hits"></td><td class="source"> </td></tr><tr class="hit"><td class="line">18</td><td class="hits">1</td><td class="source">function Storage(config) {</td></tr><tr class="hit"><td class="line">19</td><td class="hits">27</td><td class="source">    EventEmitter.call(this)</td></tr><tr><td class="line">20</td><td class="hits"></td><td class="source"> </td></tr><tr class="hit"><td class="line">21</td><td class="hits">27</td><td class="source">    this.config = config</td></tr><tr class="hit"><td class="line">22</td><td class="hits">27</td><td class="source">    this._name = config.host + ':' + config.port</td></tr><tr><td class="line">23</td><td class="hits"></td><td class="source">}</td></tr><tr><td class="line">24</td><td class="hits"></td><td class="source"> </td></tr><tr class="hit"><td class="line">25</td><td class="hits">1</td><td class="source">util.inherits(Storage, EventEmitter)</td></tr><tr><td class="line">26</td><td class="hits"></td><td class="source"> </td></tr><tr><td class="line">27</td><td class="hits"></td><td class="source">// ------------- private methods</td></tr><tr><td class="line">28</td><td class="hits"></td><td class="source"> </td></tr><tr class="hit"><td class="line">29</td><td class="hits">1</td><td class="source">Storage.prototype._getConnection = function() {</td></tr><tr class="hit"><td class="line">30</td><td class="hits">27</td><td class="source">    return this._newConnection()</td></tr><tr><td class="line">31</td><td class="hits"></td><td class="source">}</td></tr><tr><td class="line">32</td><td class="hits"></td><td class="source"> </td></tr><tr class="hit"><td class="line">33</td><td class="hits">1</td><td class="source">Storage.prototype._newConnection = function() {</td></tr><tr class="hit"><td class="line">34</td><td class="hits">27</td><td class="source">    var self = this</td></tr><tr class="hit"><td class="line">35</td><td class="hits">27</td><td class="source">    var socket = new net.Socket()</td></tr><tr class="hit"><td class="line">36</td><td class="hits">27</td><td class="source">    logger.debug('connect to storage server [%s].', this._name)</td></tr><tr class="hit"><td class="line">37</td><td class="hits">27</td><td class="source">    socket.setTimeout(this.config.timeout)</td></tr><tr class="hit"><td class="line">38</td><td class="hits">27</td><td class="source">    socket.connect(this.config.port, this.config.host)</td></tr><tr><td class="line">39</td><td class="hits"></td><td class="source"> </td></tr><tr class="hit"><td class="line">40</td><td class="hits">27</td><td class="source">    socket.on('error', function(err) {</td></tr><tr class="miss"><td class="line">41</td><td class="hits">0</td><td class="source">        self.emit('error', err)</td></tr><tr><td class="line">42</td><td class="hits"></td><td class="source">    })</td></tr><tr><td class="line">43</td><td class="hits"></td><td class="source"> </td></tr><tr class="hit"><td class="line">44</td><td class="hits">27</td><td class="source">    socket.on('timeout', function() {</td></tr><tr class="miss"><td class="line">45</td><td class="hits">0</td><td class="source">        socket.destroy()</td></tr><tr class="miss"><td class="line">46</td><td class="hits">0</td><td class="source">        self.emit('error', new Error('connect to storage server [' + self._name + '] timeout.'))</td></tr><tr><td class="line">47</td><td class="hits"></td><td class="source">    })</td></tr><tr><td class="line">48</td><td class="hits"></td><td class="source"> </td></tr><tr class="hit"><td class="line">49</td><td class="hits">27</td><td class="source">    socket.on('connect', function() {</td></tr><tr class="hit"><td class="line">50</td><td class="hits">27</td><td class="source">        logger.debug('storage server [%s] is connected', self._name)</td></tr><tr><td class="line">51</td><td class="hits"></td><td class="source">    })</td></tr><tr><td class="line">52</td><td class="hits"></td><td class="source"> </td></tr><tr class="hit"><td class="line">53</td><td class="hits">27</td><td class="source">    return socket</td></tr><tr><td class="line">54</td><td class="hits"></td><td class="source">}</td></tr><tr><td class="line">55</td><td class="hits"></td><td class="source"> </td></tr><tr><td class="line">56</td><td class="hits"></td><td class="source">// --------- upload相关</td></tr><tr><td class="line">57</td><td class="hits"></td><td class="source"> </td></tr><tr><td class="line">58</td><td class="hits"></td><td class="source">// ------------- public methods</td></tr><tr><td class="line">59</td><td class="hits"></td><td class="source"> </td></tr><tr><td class="line">60</td><td class="hits"></td><td class="source">/**</td></tr><tr><td class="line">61</td><td class="hits"></td><td class="source"> * # request body:</td></tr><tr><td class="line">62</td><td class="hits"></td><td class="source">     @ 1 byte: store path index on the storage server</td></tr><tr><td class="line">63</td><td class="hits"></td><td class="source">     @ FDFS_PROTO_PKG_LEN_SIZE bytes: file size</td></tr><tr><td class="line">64</td><td class="hits"></td><td class="source">     @ FDFS_FILE_EXT_NAME_MAX_LEN bytes: file ext name, do not include dot (.)</td></tr><tr><td class="line">65</td><td class="hits"></td><td class="source">     @ file size bytes: file content</td></tr><tr><td class="line">66</td><td class="hits"></td><td class="source"> </td></tr><tr><td class="line">67</td><td class="hits"></td><td class="source">   # response body:</td></tr><tr><td class="line">68</td><td class="hits"></td><td class="source">     @ FDFS_GROUP_NAME_MAX_LEN bytes: group name</td></tr><tr><td class="line">69</td><td class="hits"></td><td class="source">     @ filename bytes: filename</td></tr><tr><td class="line">70</td><td class="hits"></td><td class="source"> * @param file</td></tr><tr><td class="line">71</td><td class="hits"></td><td class="source"> * @param options</td></tr><tr><td class="line">72</td><td class="hits"></td><td class="source"> * @param callback</td></tr><tr><td class="line">73</td><td class="hits"></td><td class="source"> */</td></tr><tr class="hit"><td class="line">74</td><td class="hits">1</td><td class="source">Storage.prototype.upload = function(file, options, callback) {</td></tr><tr class="hit"><td class="line">75</td><td class="hits">8</td><td class="source">    var self = this</td></tr><tr class="hit"><td class="line">76</td><td class="hits">8</td><td class="source">    var socket = this._getConnection()</td></tr><tr class="hit"><td class="line">77</td><td class="hits">8</td><td class="source">    socket.on('connect', function() {</td></tr><tr class="hit"><td class="line">78</td><td class="hits">8</td><td class="source">        logger.debug('start upload file to storage server [%s]', self._name)</td></tr><tr><td class="line">79</td><td class="hits"></td><td class="source">        // ------------- 封装header并发送</td></tr><tr class="hit"><td class="line">80</td><td class="hits">8</td><td class="source">        var command = protocol.STORAGE_PROTO_CMD_UPLOAD_FILE</td></tr><tr class="hit"><td class="line">81</td><td class="hits">8</td><td class="source">        var bodyLength = 1 + protocol.FDFS_PROTO_PKG_LEN_SIZE + protocol.FDFS_FILE_EXT_NAME_MAX_LEN + options.size</td></tr><tr class="hit"><td class="line">82</td><td class="hits">8</td><td class="source">        var header = protocol.packHeader(command, bodyLength, 0)</td></tr><tr class="hit"><td class="line">83</td><td class="hits">8</td><td class="source">        socket.write(header)</td></tr><tr><td class="line">84</td><td class="hits"></td><td class="source"> </td></tr><tr><td class="line">85</td><td class="hits"></td><td class="source">        // ------------- 封装并发送body</td></tr><tr><td class="line">86</td><td class="hits"></td><td class="source">        // ------ 除file content外的内容</td></tr><tr class="hit"><td class="line">87</td><td class="hits">8</td><td class="source">        var buffer = new Buffer(1 + protocol.FDFS_PROTO_PKG_LEN_SIZE + protocol.FDFS_FILE_EXT_NAME_MAX_LEN)</td></tr><tr class="hit"><td class="line">88</td><td class="hits">8</td><td class="source">        buffer.fill(0)</td></tr><tr class="hit"><td class="line">89</td><td class="hits">8</td><td class="source">        buffer.writeUInt8(self.config.storePathIndex, 0)</td></tr><tr class="hit"><td class="line">90</td><td class="hits">8</td><td class="source">        helpers.number2Buffer(options.size, protocol.FDFS_PROTO_PKG_LEN_SIZE).copy(buffer, 1)</td></tr><tr class="hit"><td class="line">91</td><td class="hits">8</td><td class="source">        var extBL = Buffer.byteLength(options.ext, self.config.charset)</td></tr><tr class="hit"><td class="line">92</td><td class="hits">8</td><td class="source">        buffer.write(options.ext, 1 + protocol.FDFS_PROTO_PKG_LEN_SIZE, extBL, self.config.charset)</td></tr><tr><td class="line">93</td><td class="hits"></td><td class="source"> </td></tr><tr class="hit"><td class="line">94</td><td class="hits">8</td><td class="source">        socket.write(buffer)</td></tr><tr><td class="line">95</td><td class="hits"></td><td class="source"> </td></tr><tr><td class="line">96</td><td class="hits"></td><td class="source">        // ------ 发送file content</td></tr><tr class="hit"><td class="line">97</td><td class="hits">8</td><td class="source">        if (util.isString(file)) {</td></tr><tr class="hit"><td class="line">98</td><td class="hits">8</td><td class="source">            file = fs.createReadStream(file)</td></tr><tr><td class="line">99</td><td class="hits"></td><td class="source">        }</td></tr><tr><td class="line">100</td><td class="hits"></td><td class="source"> </td></tr><tr><td class="line">101</td><td class="hits"></td><td class="source">        // buffer</td></tr><tr class="hit"><td class="line">102</td><td class="hits">8</td><td class="source">        if (util.isBuffer(file)) {</td></tr><tr class="miss"><td class="line">103</td><td class="hits">0</td><td class="source">            socket.write(file)</td></tr><tr><td class="line">104</td><td class="hits"></td><td class="source"> </td></tr><tr><td class="line">105</td><td class="hits"></td><td class="source">            // stream</td></tr><tr><td class="line">106</td><td class="hits"></td><td class="source">        } else {</td></tr><tr class="hit"><td class="line">107</td><td class="hits">8</td><td class="source">            file.pipe(socket, {end: false})</td></tr><tr><td class="line">108</td><td class="hits"></td><td class="source">        }</td></tr><tr><td class="line">109</td><td class="hits"></td><td class="source">    })</td></tr><tr><td class="line">110</td><td class="hits"></td><td class="source"> </td></tr><tr class="hit"><td class="line">111</td><td class="hits">8</td><td class="source">    protocol.recvPacket(</td></tr><tr><td class="line">112</td><td class="hits"></td><td class="source">        socket,</td></tr><tr><td class="line">113</td><td class="hits"></td><td class="source">        protocol.STORAGE_PROTO_CMD_RESP,</td></tr><tr><td class="line">114</td><td class="hits"></td><td class="source">        null,</td></tr><tr><td class="line">115</td><td class="hits"></td><td class="source">        function(err, body) {</td></tr><tr class="hit"><td class="line">116</td><td class="hits">8</td><td class="source">            if (null != err) {</td></tr><tr class="miss"><td class="line">117</td><td class="hits">0</td><td class="source">                callback(err)</td></tr><tr class="miss"><td class="line">118</td><td class="hits">0</td><td class="source">                return</td></tr><tr><td class="line">119</td><td class="hits"></td><td class="source">            }</td></tr><tr><td class="line">120</td><td class="hits"></td><td class="source"> </td></tr><tr><td class="line">121</td><td class="hits"></td><td class="source">            // 校验body</td></tr><tr class="hit"><td class="line">122</td><td class="hits">8</td><td class="source">            if (body.length &lt;= protocol.FDFS_GROUP_NAME_MAX_LEN) {</td></tr><tr class="miss"><td class="line">123</td><td class="hits">0</td><td class="source">                callback(new Error('response body length: ' + body.length + ' &lt;= ' + protocol.FDFS_GROUP_NAME_MAX_LEN))</td></tr><tr class="miss"><td class="line">124</td><td class="hits">0</td><td class="source">                return</td></tr><tr><td class="line">125</td><td class="hits"></td><td class="source">            }</td></tr><tr><td class="line">126</td><td class="hits"></td><td class="source"> </td></tr><tr class="hit"><td class="line">127</td><td class="hits">8</td><td class="source">            var fileId = _parseFileId(body, self.config.charset)</td></tr><tr class="hit"><td class="line">128</td><td class="hits">8</td><td class="source">            callback(null, fileId)</td></tr><tr><td class="line">129</td><td class="hits"></td><td class="source">        })</td></tr><tr><td class="line">130</td><td class="hits"></td><td class="source">}</td></tr><tr><td class="line">131</td><td class="hits"></td><td class="source"> </td></tr><tr><td class="line">132</td><td class="hits"></td><td class="source">/**</td></tr><tr><td class="line">133</td><td class="hits"></td><td class="source"> * * STORAGE_PROTO_CMD_SET_METADATA</td></tr><tr><td class="line">134</td><td class="hits"></td><td class="source"> *</td></tr><tr><td class="line">135</td><td class="hits"></td><td class="source">     # function: set meta data</td></tr><tr><td class="line">136</td><td class="hits"></td><td class="source">     # request body:</td></tr><tr><td class="line">137</td><td class="hits"></td><td class="source">         @ FDFS_PROTO_PKG_LEN_SIZE bytes: filename length</td></tr><tr><td class="line">138</td><td class="hits"></td><td class="source">         @ FDFS_PROTO_PKG_LEN_SIZE bytes: meta data size</td></tr><tr><td class="line">139</td><td class="hits"></td><td class="source">         @ 1 bytes: operation flag,</td></tr><tr><td class="line">140</td><td class="hits"></td><td class="source">             'O' for overwrite all old metadata</td></tr><tr><td class="line">141</td><td class="hits"></td><td class="source">             'M' for merge, insert when the meta item not exist, otherwise update it</td></tr><tr><td class="line">142</td><td class="hits"></td><td class="source">         @ FDFS_GROUP_NAME_MAX_LEN bytes: group name</td></tr><tr><td class="line">143</td><td class="hits"></td><td class="source">         @ filename bytes: filename</td></tr><tr><td class="line">144</td><td class="hits"></td><td class="source">         @ meta data bytes: each meta data seperated by \x01,</td></tr><tr><td class="line">145</td><td class="hits"></td><td class="source">         name and value seperated by \x02</td></tr><tr><td class="line">146</td><td class="hits"></td><td class="source">     # response body: none</td></tr><tr><td class="line">147</td><td class="hits"></td><td class="source"> * @param fileId</td></tr><tr><td class="line">148</td><td class="hits"></td><td class="source"> * @param metaData</td></tr><tr><td class="line">149</td><td class="hits"></td><td class="source"> * @param callback</td></tr><tr><td class="line">150</td><td class="hits"></td><td class="source"> */</td></tr><tr class="hit"><td class="line">151</td><td class="hits">1</td><td class="source">Storage.prototype.setMetaData = function(fileId, metaData, flag, callback) {</td></tr><tr class="hit"><td class="line">152</td><td class="hits">5</td><td class="source">    if (!flag) flag = protocol.STORAGE_SET_METADATA_FLAG_OVERWRITE</td></tr><tr><td class="line">153</td><td class="hits"></td><td class="source"> </td></tr><tr class="hit"><td class="line">154</td><td class="hits">5</td><td class="source">    var self = this</td></tr><tr class="hit"><td class="line">155</td><td class="hits">5</td><td class="source">    var socket = this._getConnection()</td></tr><tr class="hit"><td class="line">156</td><td class="hits">5</td><td class="source">    var gf = helpers.id2gf(fileId)</td></tr><tr class="hit"><td class="line">157</td><td class="hits">5</td><td class="source">    var packedMeta = protocol.packMetaData(metaData)</td></tr><tr><td class="line">158</td><td class="hits"></td><td class="source"> </td></tr><tr class="hit"><td class="line">159</td><td class="hits">5</td><td class="source">    socket.on('connect', function() {</td></tr><tr><td class="line">160</td><td class="hits"></td><td class="source">        // ------------- 封装header</td></tr><tr class="hit"><td class="line">161</td><td class="hits">5</td><td class="source">        var charset = self.config.charset</td></tr><tr class="hit"><td class="line">162</td><td class="hits">5</td><td class="source">        var command = protocol.STORAGE_PROTO_CMD_SET_METADATA</td></tr><tr class="hit"><td class="line">163</td><td class="hits">5</td><td class="source">        var fnLength = Buffer.byteLength(gf.filename, charset)</td></tr><tr class="hit"><td class="line">164</td><td class="hits">5</td><td class="source">        var metaLength = Buffer.byteLength(packedMeta, charset)</td></tr><tr class="hit"><td class="line">165</td><td class="hits">5</td><td class="source">        var bodyLength = protocol.FDFS_PROTO_PKG_LEN_SIZE + protocol.FDFS_PROTO_PKG_LEN_SIZE + 1 +</td></tr><tr><td class="line">166</td><td class="hits"></td><td class="source">            protocol.FDFS_GROUP_NAME_MAX_LEN + fnLength + metaLength</td></tr><tr><td class="line">167</td><td class="hits"></td><td class="source"> </td></tr><tr class="hit"><td class="line">168</td><td class="hits">5</td><td class="source">        var header = protocol.packHeader(command, bodyLength, 0)</td></tr><tr class="hit"><td class="line">169</td><td class="hits">5</td><td class="source">        socket.write(header)</td></tr><tr><td class="line">170</td><td class="hits"></td><td class="source"> </td></tr><tr><td class="line">171</td><td class="hits"></td><td class="source">        // ------------- 封装body</td></tr><tr class="hit"><td class="line">172</td><td class="hits">5</td><td class="source">        var groupLength = Buffer.byteLength(gf.group, charset)</td></tr><tr class="hit"><td class="line">173</td><td class="hits">5</td><td class="source">        var body = new Buffer(bodyLength)</td></tr><tr class="hit"><td class="line">174</td><td class="hits">5</td><td class="source">        body.fill(0)</td></tr><tr class="hit"><td class="line">175</td><td class="hits">5</td><td class="source">        helpers.number2Buffer(fnLength, protocol.FDFS_PROTO_PKG_LEN_SIZE).copy(body, 0)</td></tr><tr class="hit"><td class="line">176</td><td class="hits">5</td><td class="source">        helpers.number2Buffer(metaLength, protocol.FDFS_PROTO_PKG_LEN_SIZE).copy(body, protocol.FDFS_PROTO_PKG_LEN_SIZE)</td></tr><tr class="hit"><td class="line">177</td><td class="hits">5</td><td class="source">        body.write(flag, protocol.FDFS_PROTO_PKG_LEN_SIZE + protocol.FDFS_PROTO_PKG_LEN_SIZE, 1, charset)</td></tr><tr class="hit"><td class="line">178</td><td class="hits">5</td><td class="source">        body.write(gf.group, protocol.FDFS_PROTO_PKG_LEN_SIZE + protocol.FDFS_PROTO_PKG_LEN_SIZE + 1, groupLength, charset)</td></tr><tr class="hit"><td class="line">179</td><td class="hits">5</td><td class="source">        body.write(gf.filename, protocol.FDFS_PROTO_PKG_LEN_SIZE + protocol.FDFS_PROTO_PKG_LEN_SIZE + 1 + protocol.FDFS_GROUP_NAME_MAX_LEN, fnLength, charset)</td></tr><tr class="hit"><td class="line">180</td><td class="hits">5</td><td class="source">        body.write(packedMeta, bodyLength - metaLength, metaLength, charset)</td></tr><tr><td class="line">181</td><td class="hits"></td><td class="source"> </td></tr><tr class="hit"><td class="line">182</td><td class="hits">5</td><td class="source">        socket.write(body)</td></tr><tr><td class="line">183</td><td class="hits"></td><td class="source">    })</td></tr><tr><td class="line">184</td><td class="hits"></td><td class="source"> </td></tr><tr class="hit"><td class="line">185</td><td class="hits">5</td><td class="source">    protocol.recvPacket(</td></tr><tr><td class="line">186</td><td class="hits"></td><td class="source">        socket,</td></tr><tr><td class="line">187</td><td class="hits"></td><td class="source">        protocol.STORAGE_PROTO_CMD_RESP,</td></tr><tr><td class="line">188</td><td class="hits"></td><td class="source">        0,</td></tr><tr><td class="line">189</td><td class="hits"></td><td class="source">        callback)</td></tr><tr><td class="line">190</td><td class="hits"></td><td class="source">}</td></tr><tr><td class="line">191</td><td class="hits"></td><td class="source"> </td></tr><tr><td class="line">192</td><td class="hits"></td><td class="source">/**</td></tr><tr><td class="line">193</td><td class="hits"></td><td class="source"> * * STORAGE_PROTO_CMD_GET_METADATA</td></tr><tr><td class="line">194</td><td class="hits"></td><td class="source">     # function: get metat data from storage server</td></tr><tr><td class="line">195</td><td class="hits"></td><td class="source">     # request body:</td></tr><tr><td class="line">196</td><td class="hits"></td><td class="source">     @ FDFS_GROUP_NAME_MAX_LEN bytes: group name</td></tr><tr><td class="line">197</td><td class="hits"></td><td class="source">     @ filename bytes: filename</td></tr><tr><td class="line">198</td><td class="hits"></td><td class="source">     # response body</td></tr><tr><td class="line">199</td><td class="hits"></td><td class="source">     @ meta data buff, each meta data seperated by \x01, name and value seperated by \x02</td></tr><tr><td class="line">200</td><td class="hits"></td><td class="source"> * @param fileId</td></tr><tr><td class="line">201</td><td class="hits"></td><td class="source"> * @param callback</td></tr><tr><td class="line">202</td><td class="hits"></td><td class="source"> */</td></tr><tr class="hit"><td class="line">203</td><td class="hits">1</td><td class="source">Storage.prototype.getMetaData = function(fileId, callback) {</td></tr><tr class="hit"><td class="line">204</td><td class="hits">3</td><td class="source">    var self = this</td></tr><tr class="hit"><td class="line">205</td><td class="hits">3</td><td class="source">    var gf = helpers.id2gf(fileId)</td></tr><tr class="hit"><td class="line">206</td><td class="hits">3</td><td class="source">    var socket = this._getConnection()</td></tr><tr class="hit"><td class="line">207</td><td class="hits">3</td><td class="source">    socket.on('connect', function() {</td></tr><tr class="hit"><td class="line">208</td><td class="hits">3</td><td class="source">        var packet = protocol.packFileId(protocol.STORAGE_PROTO_CMD_GET_METADATA, gf.group, gf.filename, self.config.charset)</td></tr><tr class="hit"><td class="line">209</td><td class="hits">3</td><td class="source">        socket.write(packet)</td></tr><tr><td class="line">210</td><td class="hits"></td><td class="source">    })</td></tr><tr><td class="line">211</td><td class="hits"></td><td class="source"> </td></tr><tr class="hit"><td class="line">212</td><td class="hits">3</td><td class="source">    protocol.recvPacket(</td></tr><tr><td class="line">213</td><td class="hits"></td><td class="source">        socket,</td></tr><tr><td class="line">214</td><td class="hits"></td><td class="source">        protocol.STORAGE_PROTO_CMD_RESP,</td></tr><tr><td class="line">215</td><td class="hits"></td><td class="source">        null,</td></tr><tr><td class="line">216</td><td class="hits"></td><td class="source">        function(err, body) {</td></tr><tr class="hit"><td class="line">217</td><td class="hits">3</td><td class="source">            if (err) {</td></tr><tr class="miss"><td class="line">218</td><td class="hits">0</td><td class="source">                callback(err)</td></tr><tr class="miss"><td class="line">219</td><td class="hits">0</td><td class="source">                return</td></tr><tr><td class="line">220</td><td class="hits"></td><td class="source">            }</td></tr><tr><td class="line">221</td><td class="hits"></td><td class="source"> </td></tr><tr class="hit"><td class="line">222</td><td class="hits">3</td><td class="source">            var rawMeta = body.toString(self.config.charset)</td></tr><tr class="hit"><td class="line">223</td><td class="hits">3</td><td class="source">            if (rawMeta) {</td></tr><tr class="hit"><td class="line">224</td><td class="hits">3</td><td class="source">                var metaData = protocol.parseMetaData(rawMeta)</td></tr><tr class="hit"><td class="line">225</td><td class="hits">3</td><td class="source">                callback(null, metaData)</td></tr><tr><td class="line">226</td><td class="hits"></td><td class="source"> </td></tr><tr><td class="line">227</td><td class="hits"></td><td class="source">            } else {</td></tr><tr class="miss"><td class="line">228</td><td class="hits">0</td><td class="source">                callback(null, rawMeta)</td></tr><tr><td class="line">229</td><td class="hits"></td><td class="source">            }</td></tr><tr><td class="line">230</td><td class="hits"></td><td class="source">        })</td></tr><tr><td class="line">231</td><td class="hits"></td><td class="source">}</td></tr><tr><td class="line">232</td><td class="hits"></td><td class="source"> </td></tr><tr><td class="line">233</td><td class="hits"></td><td class="source">/**</td></tr><tr><td class="line">234</td><td class="hits"></td><td class="source"> * 删除文件</td></tr><tr><td class="line">235</td><td class="hits"></td><td class="source"> * STORAGE_PROTO_CMD_DELETE_FILE</td></tr><tr><td class="line">236</td><td class="hits"></td><td class="source"> * # request body:</td></tr><tr><td class="line">237</td><td class="hits"></td><td class="source">     @ FDFS_GROUP_NAME_MAX_LEN bytes: group name</td></tr><tr><td class="line">238</td><td class="hits"></td><td class="source">     @ filename bytes: filename</td></tr><tr><td class="line">239</td><td class="hits"></td><td class="source"> </td></tr><tr><td class="line">240</td><td class="hits"></td><td class="source">   # response body: none</td></tr><tr><td class="line">241</td><td class="hits"></td><td class="source"> * @param fileId</td></tr><tr><td class="line">242</td><td class="hits"></td><td class="source"> * @param callback</td></tr><tr><td class="line">243</td><td class="hits"></td><td class="source"> */</td></tr><tr class="hit"><td class="line">244</td><td class="hits">1</td><td class="source">Storage.prototype.del = function(fileId, callback) {</td></tr><tr class="hit"><td class="line">245</td><td class="hits">7</td><td class="source">    var self = this</td></tr><tr class="hit"><td class="line">246</td><td class="hits">7</td><td class="source">    var gf = helpers.id2gf(fileId)</td></tr><tr class="hit"><td class="line">247</td><td class="hits">7</td><td class="source">    var socket = this._getConnection()</td></tr><tr><td class="line">248</td><td class="hits"></td><td class="source"> </td></tr><tr class="hit"><td class="line">249</td><td class="hits">7</td><td class="source">    socket.on('connect', function() {</td></tr><tr class="hit"><td class="line">250</td><td class="hits">7</td><td class="source">        var packet = protocol.packFileId(protocol.STORAGE_PROTO_CMD_DELETE_FILE, gf.group, gf.filename, self.config.charset)</td></tr><tr class="hit"><td class="line">251</td><td class="hits">7</td><td class="source">        socket.write(packet)</td></tr><tr><td class="line">252</td><td class="hits"></td><td class="source">    })</td></tr><tr><td class="line">253</td><td class="hits"></td><td class="source"> </td></tr><tr class="hit"><td class="line">254</td><td class="hits">7</td><td class="source">    protocol.recvPacket(</td></tr><tr><td class="line">255</td><td class="hits"></td><td class="source">        socket,</td></tr><tr><td class="line">256</td><td class="hits"></td><td class="source">        protocol.STORAGE_PROTO_CMD_RESP,</td></tr><tr><td class="line">257</td><td class="hits"></td><td class="source">        0,</td></tr><tr><td class="line">258</td><td class="hits"></td><td class="source">        callback)</td></tr><tr><td class="line">259</td><td class="hits"></td><td class="source">}</td></tr><tr><td class="line">260</td><td class="hits"></td><td class="source"> </td></tr><tr><td class="line">261</td><td class="hits"></td><td class="source">/**</td></tr><tr><td class="line">262</td><td class="hits"></td><td class="source"> * STORAGE_PROTO_CMD_DOWNLOAD_FILE</td></tr><tr><td class="line">263</td><td class="hits"></td><td class="source">     # function: download/fetch file from storage server</td></tr><tr><td class="line">264</td><td class="hits"></td><td class="source">     # request body:</td></tr><tr><td class="line">265</td><td class="hits"></td><td class="source">         @ FDFS_PROTO_PKG_LEN_SIZE bytes: file offset</td></tr><tr><td class="line">266</td><td class="hits"></td><td class="source">         @ FDFS_PROTO_PKG_LEN_SIZE bytes: download file bytes</td></tr><tr><td class="line">267</td><td class="hits"></td><td class="source">         @ FDFS_GROUP_NAME_MAX_LEN bytes: group name</td></tr><tr><td class="line">268</td><td class="hits"></td><td class="source">         @ filename bytes: filename</td></tr><tr><td class="line">269</td><td class="hits"></td><td class="source"> </td></tr><tr><td class="line">270</td><td class="hits"></td><td class="source">     # response body:</td></tr><tr><td class="line">271</td><td class="hits"></td><td class="source">        @ file content</td></tr><tr><td class="line">272</td><td class="hits"></td><td class="source"> * @param fileId</td></tr><tr><td class="line">273</td><td class="hits"></td><td class="source"> * @param options</td></tr><tr><td class="line">274</td><td class="hits"></td><td class="source"> *      options.target 下载的文件流将被写入到这里，可以是本地文件名，也可以是WritableStream，如果为空则每次服务器返回数据的时候都会回调callback</td></tr><tr><td class="line">275</td><td class="hits"></td><td class="source"> *      options.offset和options.bytes: 当只想下载文件中的某1片段时指定</td></tr><tr><td class="line">276</td><td class="hits"></td><td class="source"> * @param callback 若未指定options.target，服务器每次数据的返回都会回调，若指定了options.target，则只在结束时回调一次</td></tr><tr><td class="line">277</td><td class="hits"></td><td class="source"> */</td></tr><tr class="hit"><td class="line">278</td><td class="hits">1</td><td class="source">Storage.prototype.download = function(fileId, options, callback) {</td></tr><tr class="hit"><td class="line">279</td><td class="hits">3</td><td class="source">    var self = this</td></tr><tr class="hit"><td class="line">280</td><td class="hits">3</td><td class="source">    var gf = helpers.id2gf(fileId)</td></tr><tr class="hit"><td class="line">281</td><td class="hits">3</td><td class="source">    var socket = this._getConnection()</td></tr><tr class="hit"><td class="line">282</td><td class="hits">3</td><td class="source">    socket.on('connect', function() {</td></tr><tr class="hit"><td class="line">283</td><td class="hits">3</td><td class="source">        var charset = self.config.charset</td></tr><tr><td class="line">284</td><td class="hits"></td><td class="source">        // --------- 封装header</td></tr><tr class="hit"><td class="line">285</td><td class="hits">3</td><td class="source">        var fnLength = Buffer.byteLength(gf.filename, charset)</td></tr><tr class="hit"><td class="line">286</td><td class="hits">3</td><td class="source">        var bodyLength = protocol.FDFS_PROTO_PKG_LEN_SIZE + protocol.FDFS_PROTO_PKG_LEN_SIZE + protocol.FDFS_GROUP_NAME_MAX_LEN + fnLength</td></tr><tr class="hit"><td class="line">287</td><td class="hits">3</td><td class="source">        var header = protocol.packHeader(protocol.STORAGE_PROTO_CMD_DOWNLOAD_FILE, bodyLength, 0)</td></tr><tr><td class="line">288</td><td class="hits"></td><td class="source"> </td></tr><tr><td class="line">289</td><td class="hits"></td><td class="source">        // --------- 封装body</td></tr><tr class="hit"><td class="line">290</td><td class="hits">3</td><td class="source">        var body = new Buffer(bodyLength)</td></tr><tr><td class="line">291</td><td class="hits"></td><td class="source">        // 默认都填充上0</td></tr><tr class="hit"><td class="line">292</td><td class="hits">3</td><td class="source">        body.fill(0)</td></tr><tr class="hit"><td class="line">293</td><td class="hits">3</td><td class="source">        if (options.offset) {</td></tr><tr class="hit"><td class="line">294</td><td class="hits">1</td><td class="source">            helpers.number2Buffer(options.offset, protocol.FDFS_PROTO_PKG_LEN_SIZE).copy(body)</td></tr><tr><td class="line">295</td><td class="hits"></td><td class="source">        }</td></tr><tr class="hit"><td class="line">296</td><td class="hits">3</td><td class="source">        if (options.bytes) {</td></tr><tr class="hit"><td class="line">297</td><td class="hits">1</td><td class="source">            helpers.number2Buffer(options.bytes, protocol.FDFS_PROTO_PKG_LEN_SIZE).copy(body, protocol.FDFS_PROTO_PKG_LEN_SIZE)</td></tr><tr><td class="line">298</td><td class="hits"></td><td class="source">        }</td></tr><tr class="hit"><td class="line">299</td><td class="hits">3</td><td class="source">        var groupBL = Buffer.byteLength(gf.group, charset)</td></tr><tr class="hit"><td class="line">300</td><td class="hits">3</td><td class="source">        body.write(gf.group, protocol.FDFS_PROTO_PKG_LEN_SIZE + protocol.FDFS_PROTO_PKG_LEN_SIZE, groupBL, charset)</td></tr><tr class="hit"><td class="line">301</td><td class="hits">3</td><td class="source">        body.write(gf.filename, protocol.FDFS_PROTO_PKG_LEN_SIZE + protocol.FDFS_PROTO_PKG_LEN_SIZE + protocol.FDFS_GROUP_NAME_MAX_LEN, fnLength, charset)</td></tr><tr><td class="line">302</td><td class="hits"></td><td class="source"> </td></tr><tr class="hit"><td class="line">303</td><td class="hits">3</td><td class="source">        socket.write(Buffer.concat([header, body]))</td></tr><tr><td class="line">304</td><td class="hits"></td><td class="source">    })</td></tr><tr><td class="line">305</td><td class="hits"></td><td class="source"> </td></tr><tr class="hit"><td class="line">306</td><td class="hits">3</td><td class="source">    var header</td></tr><tr class="hit"><td class="line">307</td><td class="hits">3</td><td class="source">    var target = options.target</td></tr><tr><td class="line">308</td><td class="hits"></td><td class="source">    // 已接收的body length</td></tr><tr class="hit"><td class="line">309</td><td class="hits">3</td><td class="source">    var recvLength = 0</td></tr><tr><td class="line">310</td><td class="hits"></td><td class="source"> </td></tr><tr class="hit"><td class="line">311</td><td class="hits">3</td><td class="source">    protocol.recvPacket(</td></tr><tr><td class="line">312</td><td class="hits"></td><td class="source">        socket,</td></tr><tr><td class="line">313</td><td class="hits"></td><td class="source">        protocol.STORAGE_PROTO_CMD_RESP,</td></tr><tr><td class="line">314</td><td class="hits"></td><td class="source">        null,</td></tr><tr><td class="line">315</td><td class="hits"></td><td class="source">        function(err, data) {</td></tr><tr class="hit"><td class="line">316</td><td class="hits">5</td><td class="source">            if (err) {</td></tr><tr class="hit"><td class="line">317</td><td class="hits">1</td><td class="source">                callback(err)</td></tr><tr class="hit"><td class="line">318</td><td class="hits">1</td><td class="source">                return</td></tr><tr><td class="line">319</td><td class="hits"></td><td class="source">            }</td></tr><tr><td class="line">320</td><td class="hits"></td><td class="source"> </td></tr><tr><td class="line">321</td><td class="hits"></td><td class="source">            // header</td></tr><tr class="hit"><td class="line">322</td><td class="hits">4</td><td class="source">            if (!util.isBuffer(data)) {</td></tr><tr class="hit"><td class="line">323</td><td class="hits">2</td><td class="source">                header = data</td></tr><tr><td class="line">324</td><td class="hits"></td><td class="source"> </td></tr><tr><td class="line">325</td><td class="hits"></td><td class="source">            // body</td></tr><tr><td class="line">326</td><td class="hits"></td><td class="source">            } else {</td></tr><tr class="hit"><td class="line">327</td><td class="hits">2</td><td class="source">                if (!target) {</td></tr><tr class="miss"><td class="line">328</td><td class="hits">0</td><td class="source">                    callback(null, data, header.bodyLength)</td></tr><tr><td class="line">329</td><td class="hits"></td><td class="source"> </td></tr><tr><td class="line">330</td><td class="hits"></td><td class="source">                } else {</td></tr><tr class="hit"><td class="line">331</td><td class="hits">2</td><td class="source">                    target.write(data)</td></tr><tr><td class="line">332</td><td class="hits"></td><td class="source">                }</td></tr><tr><td class="line">333</td><td class="hits"></td><td class="source"> </td></tr><tr class="hit"><td class="line">334</td><td class="hits">2</td><td class="source">                recvLength += data.length</td></tr><tr><td class="line">335</td><td class="hits"></td><td class="source">                // 读取完毕</td></tr><tr class="hit"><td class="line">336</td><td class="hits">2</td><td class="source">                if (recvLength &gt;= header.bodyLength) {</td></tr><tr class="hit"><td class="line">337</td><td class="hits">2</td><td class="source">                    protocol.closeSocket(socket)</td></tr><tr class="hit"><td class="line">338</td><td class="hits">2</td><td class="source">                    callback(null)</td></tr><tr><td class="line">339</td><td class="hits"></td><td class="source">                }</td></tr><tr><td class="line">340</td><td class="hits"></td><td class="source">            }</td></tr><tr><td class="line">341</td><td class="hits"></td><td class="source">        },</td></tr><tr><td class="line">342</td><td class="hits"></td><td class="source">        true)</td></tr><tr><td class="line">343</td><td class="hits"></td><td class="source">}</td></tr><tr><td class="line">344</td><td class="hits"></td><td class="source"> </td></tr><tr><td class="line">345</td><td class="hits"></td><td class="source">/**</td></tr><tr><td class="line">346</td><td class="hits"></td><td class="source"> * * STORAGE_PROTO_CMD_QUERY_FILE_INFO</td></tr><tr><td class="line">347</td><td class="hits"></td><td class="source">     # function: query file info from storage server</td></tr><tr><td class="line">348</td><td class="hits"></td><td class="source">     # request body:</td></tr><tr><td class="line">349</td><td class="hits"></td><td class="source">        @ FDFS_GROUP_NAME_MAX_LEN bytes: group name</td></tr><tr><td class="line">350</td><td class="hits"></td><td class="source">        @ filename bytes: filename</td></tr><tr><td class="line">351</td><td class="hits"></td><td class="source"> </td></tr><tr><td class="line">352</td><td class="hits"></td><td class="source">     # response body:</td></tr><tr><td class="line">353</td><td class="hits"></td><td class="source">        @ FDFS_PROTO_PKG_LEN_SIZE bytes: file size</td></tr><tr><td class="line">354</td><td class="hits"></td><td class="source">        @ FDFS_PROTO_PKG_LEN_SIZE bytes: file create timestamp</td></tr><tr><td class="line">355</td><td class="hits"></td><td class="source">        @ FDFS_PROTO_PKG_LEN_SIZE bytes: file CRC32 signature</td></tr><tr><td class="line">356</td><td class="hits"></td><td class="source">        @ FDFS_IPADDR_SIZE bytes: file source ip addr</td></tr><tr><td class="line">357</td><td class="hits"></td><td class="source"> * @param fileId</td></tr><tr><td class="line">358</td><td class="hits"></td><td class="source"> * @param callback</td></tr><tr><td class="line">359</td><td class="hits"></td><td class="source"> */</td></tr><tr class="hit"><td class="line">360</td><td class="hits">1</td><td class="source">Storage.prototype.getFileInfo = function(fileId, callback) {</td></tr><tr class="hit"><td class="line">361</td><td class="hits">1</td><td class="source">    var self = this</td></tr><tr class="hit"><td class="line">362</td><td class="hits">1</td><td class="source">    var gf = helpers.id2gf(fileId)</td></tr><tr class="hit"><td class="line">363</td><td class="hits">1</td><td class="source">    var socket = this._getConnection()</td></tr><tr class="hit"><td class="line">364</td><td class="hits">1</td><td class="source">    var charset = this.config.charset</td></tr><tr><td class="line">365</td><td class="hits"></td><td class="source"> </td></tr><tr class="hit"><td class="line">366</td><td class="hits">1</td><td class="source">    socket.on('connect', function() {</td></tr><tr class="hit"><td class="line">367</td><td class="hits">1</td><td class="source">        var packet = protocol.packFileId(protocol.STORAGE_PROTO_CMD_QUERY_FILE_INFO, gf.group, gf.filename, charset)</td></tr><tr class="hit"><td class="line">368</td><td class="hits">1</td><td class="source">        socket.write(packet)</td></tr><tr><td class="line">369</td><td class="hits"></td><td class="source">    })</td></tr><tr><td class="line">370</td><td class="hits"></td><td class="source"> </td></tr><tr class="hit"><td class="line">371</td><td class="hits">1</td><td class="source">    protocol.recvPacket(</td></tr><tr><td class="line">372</td><td class="hits"></td><td class="source">        socket,</td></tr><tr><td class="line">373</td><td class="hits"></td><td class="source">        protocol.STORAGE_PROTO_CMD_RESP,</td></tr><tr><td class="line">374</td><td class="hits"></td><td class="source">        protocol.FDFS_PROTO_PKG_LEN_SIZE * 3 + protocol.FDFS_IPADDR_SIZE,</td></tr><tr><td class="line">375</td><td class="hits"></td><td class="source">        function(err, body) {</td></tr><tr class="hit"><td class="line">376</td><td class="hits">1</td><td class="source">            if (err) {</td></tr><tr class="miss"><td class="line">377</td><td class="hits">0</td><td class="source">                callback(err)</td></tr><tr class="miss"><td class="line">378</td><td class="hits">0</td><td class="source">                return</td></tr><tr><td class="line">379</td><td class="hits"></td><td class="source">            }</td></tr><tr><td class="line">380</td><td class="hits"></td><td class="source"> </td></tr><tr class="hit"><td class="line">381</td><td class="hits">1</td><td class="source">            var result = {}</td></tr><tr><td class="line">382</td><td class="hits"></td><td class="source"> </td></tr><tr class="hit"><td class="line">383</td><td class="hits">1</td><td class="source">            result.size = new BigNumber(body.toString('hex', 0, protocol.FDFS_PROTO_PKG_LEN_SIZE), 16).toString(10)</td></tr><tr class="hit"><td class="line">384</td><td class="hits">1</td><td class="source">            result.timestamp = new BigNumber(body.toString('hex', protocol.FDFS_PROTO_PKG_LEN_SIZE, protocol.FDFS_PROTO_PKG_LEN_SIZE * 2), 16).toNumber()</td></tr><tr class="hit"><td class="line">385</td><td class="hits">1</td><td class="source">            result.crc32 = new BigNumber(body.toString('hex', protocol.FDFS_PROTO_PKG_LEN_SIZE * 2, protocol.FDFS_PROTO_PKG_LEN_SIZE * 3), 16).toNumber()</td></tr><tr class="hit"><td class="line">386</td><td class="hits">1</td><td class="source">            result.addr = helpers.trim(body.toString(charset, protocol.FDFS_PROTO_PKG_LEN_SIZE * 3))</td></tr><tr><td class="line">387</td><td class="hits"></td><td class="source"> </td></tr><tr class="hit"><td class="line">388</td><td class="hits">1</td><td class="source">            callback(null, result)</td></tr><tr><td class="line">389</td><td class="hits"></td><td class="source">        })</td></tr><tr><td class="line">390</td><td class="hits"></td><td class="source">}</td></tr><tr><td class="line">391</td><td class="hits"></td><td class="source"> </td></tr><tr><td class="line">392</td><td class="hits"></td><td class="source">// -------------- helpers</td></tr><tr><td class="line">393</td><td class="hits"></td><td class="source">/**</td></tr><tr><td class="line">394</td><td class="hits"></td><td class="source"> * parse file id from body</td></tr><tr><td class="line">395</td><td class="hits"></td><td class="source"> * @param body</td></tr><tr><td class="line">396</td><td class="hits"></td><td class="source"> * @param charset</td></tr><tr><td class="line">397</td><td class="hits"></td><td class="source"> * @private</td></tr><tr><td class="line">398</td><td class="hits"></td><td class="source"> */</td></tr><tr class="hit"><td class="line">399</td><td class="hits">1</td><td class="source">function _parseFileId(body, charset) {</td></tr><tr class="hit"><td class="line">400</td><td class="hits">8</td><td class="source">    var group = helpers.trim(body.toString(charset, 0, protocol.FDFS_GROUP_NAME_MAX_LEN))</td></tr><tr class="hit"><td class="line">401</td><td class="hits">8</td><td class="source">    var filename = helpers.trim(body.toString(charset, protocol.FDFS_GROUP_NAME_MAX_LEN))</td></tr><tr><td class="line">402</td><td class="hits"></td><td class="source"> </td></tr><tr class="hit"><td class="line">403</td><td class="hits">8</td><td class="source">    return group + '/' + filename</td></tr><tr><td class="line">404</td><td class="hits"></td><td class="source">}</td></tr><tr><td class="line">405</td><td class="hits"></td><td class="source"> </td></tr><tr class="hit"><td class="line">406</td><td class="hits">1</td><td class="source">module.exports = Storage</td></tr></tbody></table></div><div class="file"><h2 id="/Users/chenboxiang/WebstormProjects/fdfs-client/lib/tracker.js">/Users/chenboxiang/WebstormProjects/fdfs-client/lib/tracker.js</h2><div id="stats" class="high"><div class="percentage">92%</div><div class="sloc">105</div><div class="hits">97</div><div class="misses">8</div></div><table id="source"><thead><tr><th>Line</th><th>Hits</th><th>Source</th></tr></thead><tbody><tr><td class="line">1</td><td class="hits"></td><td class="source">/**</td></tr><tr><td class="line">2</td><td class="hits"></td><td class="source"> * Author: chenboxiang</td></tr><tr><td class="line">3</td><td class="hits"></td><td class="source"> * Date: 14-6-13</td></tr><tr><td class="line">4</td><td class="hits"></td><td class="source"> * Time: 下午10:17</td></tr><tr><td class="line">5</td><td class="hits"></td><td class="source"> */</td></tr><tr class="hit"><td class="line">6</td><td class="hits">1</td><td class="source">'use strict'</td></tr><tr><td class="line">7</td><td class="hits"></td><td class="source"> </td></tr><tr class="hit"><td class="line">8</td><td class="hits">1</td><td class="source">var Buffer = require('buffer').Buffer</td></tr><tr class="hit"><td class="line">9</td><td class="hits">1</td><td class="source">var net = require('net')</td></tr><tr class="hit"><td class="line">10</td><td class="hits">1</td><td class="source">var EventEmitter = require('events').EventEmitter</td></tr><tr class="hit"><td class="line">11</td><td class="hits">1</td><td class="source">var util = require('util')</td></tr><tr class="hit"><td class="line">12</td><td class="hits">1</td><td class="source">var logger = require('./logger')</td></tr><tr class="hit"><td class="line">13</td><td class="hits">1</td><td class="source">var protocol = require('./protocol')</td></tr><tr class="hit"><td class="line">14</td><td class="hits">1</td><td class="source">var Storage = require('./storage')</td></tr><tr class="hit"><td class="line">15</td><td class="hits">1</td><td class="source">var helpers = require('./helpers')</td></tr><tr><td class="line">16</td><td class="hits"></td><td class="source"> </td></tr><tr><td class="line">17</td><td class="hits"></td><td class="source"> </td></tr><tr class="hit"><td class="line">18</td><td class="hits">1</td><td class="source">function Tracker(config) {</td></tr><tr class="hit"><td class="line">19</td><td class="hits">1</td><td class="source">    EventEmitter.call(this)</td></tr><tr><td class="line">20</td><td class="hits"></td><td class="source"> </td></tr><tr class="hit"><td class="line">21</td><td class="hits">1</td><td class="source">    this.config = config</td></tr><tr class="hit"><td class="line">22</td><td class="hits">1</td><td class="source">    this._name = config.host + ':' + config.port</td></tr><tr><td class="line">23</td><td class="hits"></td><td class="source">}</td></tr><tr><td class="line">24</td><td class="hits"></td><td class="source"> </td></tr><tr><td class="line">25</td><td class="hits"></td><td class="source">// extends from EventEmitter</td></tr><tr class="hit"><td class="line">26</td><td class="hits">1</td><td class="source">util.inherits(Tracker, EventEmitter)</td></tr><tr><td class="line">27</td><td class="hits"></td><td class="source"> </td></tr><tr><td class="line">28</td><td class="hits"></td><td class="source">// ---------------- private methods</td></tr><tr><td class="line">29</td><td class="hits"></td><td class="source"> </td></tr><tr class="hit"><td class="line">30</td><td class="hits">1</td><td class="source">Tracker.prototype._getConnection = function() {</td></tr><tr class="hit"><td class="line">31</td><td class="hits">27</td><td class="source">    return this._newConnection()</td></tr><tr><td class="line">32</td><td class="hits"></td><td class="source">}</td></tr><tr><td class="line">33</td><td class="hits"></td><td class="source"> </td></tr><tr class="hit"><td class="line">34</td><td class="hits">1</td><td class="source">Tracker.prototype._newConnection = function() {</td></tr><tr class="hit"><td class="line">35</td><td class="hits">27</td><td class="source">    var self = this</td></tr><tr class="hit"><td class="line">36</td><td class="hits">27</td><td class="source">    var socket = new net.Socket()</td></tr><tr class="hit"><td class="line">37</td><td class="hits">27</td><td class="source">    logger.debug('connect to tracker server [%s]', this._name)</td></tr><tr class="hit"><td class="line">38</td><td class="hits">27</td><td class="source">    socket.setTimeout(this.config.timeout)</td></tr><tr class="hit"><td class="line">39</td><td class="hits">27</td><td class="source">    socket.connect(this.config.port, this.config.host)</td></tr><tr><td class="line">40</td><td class="hits"></td><td class="source"> </td></tr><tr class="hit"><td class="line">41</td><td class="hits">27</td><td class="source">    socket.on('error', function(err) {</td></tr><tr class="miss"><td class="line">42</td><td class="hits">0</td><td class="source">        self.emit('error', err)</td></tr><tr><td class="line">43</td><td class="hits"></td><td class="source">    })</td></tr><tr><td class="line">44</td><td class="hits"></td><td class="source"> </td></tr><tr class="hit"><td class="line">45</td><td class="hits">27</td><td class="source">    socket.on('timeout', function() {</td></tr><tr class="miss"><td class="line">46</td><td class="hits">0</td><td class="source">        socket.destroy()</td></tr><tr class="miss"><td class="line">47</td><td class="hits">0</td><td class="source">        self.emit('error', new Error('connect to tracker server [' + self._name + '] timeout.'))</td></tr><tr><td class="line">48</td><td class="hits"></td><td class="source">    })</td></tr><tr><td class="line">49</td><td class="hits"></td><td class="source"> </td></tr><tr class="hit"><td class="line">50</td><td class="hits">27</td><td class="source">    socket.on('connect', function() {</td></tr><tr class="hit"><td class="line">51</td><td class="hits">27</td><td class="source">        logger.debug('tracker server [%s] is connected', self._name)</td></tr><tr><td class="line">52</td><td class="hits"></td><td class="source">    })</td></tr><tr><td class="line">53</td><td class="hits"></td><td class="source"> </td></tr><tr class="hit"><td class="line">54</td><td class="hits">27</td><td class="source">    return socket</td></tr><tr><td class="line">55</td><td class="hits"></td><td class="source">}</td></tr><tr><td class="line">56</td><td class="hits"></td><td class="source"> </td></tr><tr><td class="line">57</td><td class="hits"></td><td class="source">// ---------------- public methods</td></tr><tr><td class="line">58</td><td class="hits"></td><td class="source"> </td></tr><tr><td class="line">59</td><td class="hits"></td><td class="source">/**</td></tr><tr><td class="line">60</td><td class="hits"></td><td class="source"> * query storage server to upload file</td></tr><tr><td class="line">61</td><td class="hits"></td><td class="source"> * 获取指定group的storage实例，如果不指定则tracker server会随机返回1个</td></tr><tr><td class="line">62</td><td class="hits"></td><td class="source"> * @param group</td></tr><tr><td class="line">63</td><td class="hits"></td><td class="source"> */</td></tr><tr class="hit"><td class="line">64</td><td class="hits">1</td><td class="source">Tracker.prototype.getStoreStorage = function(group, callback) {</td></tr><tr><td class="line">65</td><td class="hits"></td><td class="source">    // 验证group是否过长</td></tr><tr class="hit"><td class="line">66</td><td class="hits">8</td><td class="source">    if (group &amp;&amp; group.length &gt; protocol.FDFS_GROUP_NAME_MAX_LEN) {</td></tr><tr class="miss"><td class="line">67</td><td class="hits">0</td><td class="source">        throw new Error('group name [' + group + '] is too long')</td></tr><tr><td class="line">68</td><td class="hits"></td><td class="source">    }</td></tr><tr class="hit"><td class="line">69</td><td class="hits">8</td><td class="source">    logger.debug('get a upload storage server from tracker server: [%s]', this._name)</td></tr><tr><td class="line">70</td><td class="hits"></td><td class="source"> </td></tr><tr class="hit"><td class="line">71</td><td class="hits">8</td><td class="source">    var self = this</td></tr><tr class="hit"><td class="line">72</td><td class="hits">8</td><td class="source">    var socket = this._getConnection()</td></tr><tr class="hit"><td class="line">73</td><td class="hits">8</td><td class="source">    socket.on('connect', function() {</td></tr><tr><td class="line">74</td><td class="hits"></td><td class="source">        // ----------- 获取1个可用storage server 信息</td></tr><tr><td class="line">75</td><td class="hits"></td><td class="source">        // -------- 封装header并发送</td></tr><tr class="hit"><td class="line">76</td><td class="hits">8</td><td class="source">        var command</td></tr><tr class="hit"><td class="line">77</td><td class="hits">8</td><td class="source">        var bodyLength</td></tr><tr class="hit"><td class="line">78</td><td class="hits">8</td><td class="source">        if (!group) {</td></tr><tr class="hit"><td class="line">79</td><td class="hits">7</td><td class="source">            command = protocol.TRACKER_PROTO_CMD_SERVICE_QUERY_STORE_WITHOUT_GROUP_ONE</td></tr><tr class="hit"><td class="line">80</td><td class="hits">7</td><td class="source">            bodyLength = 0</td></tr><tr><td class="line">81</td><td class="hits"></td><td class="source"> </td></tr><tr><td class="line">82</td><td class="hits"></td><td class="source">        } else {</td></tr><tr class="hit"><td class="line">83</td><td class="hits">1</td><td class="source">            command = protocol.TRACKER_PROTO_CMD_SERVICE_QUERY_STORE_WITH_GROUP_ONE</td></tr><tr class="hit"><td class="line">84</td><td class="hits">1</td><td class="source">            bodyLength = protocol.FDFS_GROUP_NAME_MAX_LEN</td></tr><tr><td class="line">85</td><td class="hits"></td><td class="source">        }</td></tr><tr class="hit"><td class="line">86</td><td class="hits">8</td><td class="source">        var header = protocol.packHeader(command, bodyLength, 0)</td></tr><tr class="hit"><td class="line">87</td><td class="hits">8</td><td class="source">        logger.debug('send header to tracker server [%s]', self._name)</td></tr><tr class="hit"><td class="line">88</td><td class="hits">8</td><td class="source">        socket.write(header)</td></tr><tr><td class="line">89</td><td class="hits"></td><td class="source"> </td></tr><tr><td class="line">90</td><td class="hits"></td><td class="source">        // -------- 发送body</td></tr><tr class="hit"><td class="line">91</td><td class="hits">8</td><td class="source">        if (group) {</td></tr><tr class="hit"><td class="line">92</td><td class="hits">1</td><td class="source">            var body = new Buffer(bodyLength)</td></tr><tr><td class="line">93</td><td class="hits"></td><td class="source">            // 默认都填充上0</td></tr><tr class="hit"><td class="line">94</td><td class="hits">1</td><td class="source">            body.fill(0)</td></tr><tr class="hit"><td class="line">95</td><td class="hits">1</td><td class="source">            var groupBL = Buffer.byteLength(group, self.config.charset)</td></tr><tr class="hit"><td class="line">96</td><td class="hits">1</td><td class="source">            body.write(group, 0, groupBL, self.config.charset)</td></tr><tr class="hit"><td class="line">97</td><td class="hits">1</td><td class="source">            logger.debug('send body to tracker server [%s]', self._name)</td></tr><tr class="hit"><td class="line">98</td><td class="hits">1</td><td class="source">            socket.write(body)</td></tr><tr><td class="line">99</td><td class="hits"></td><td class="source">        }</td></tr><tr><td class="line">100</td><td class="hits"></td><td class="source">    })</td></tr><tr><td class="line">101</td><td class="hits"></td><td class="source"> </td></tr><tr class="hit"><td class="line">102</td><td class="hits">8</td><td class="source">    protocol.recvPacket(</td></tr><tr><td class="line">103</td><td class="hits"></td><td class="source">        socket,</td></tr><tr><td class="line">104</td><td class="hits"></td><td class="source">        protocol.TRACKER_PROTO_CMD_RESP,</td></tr><tr><td class="line">105</td><td class="hits"></td><td class="source">        protocol.TRACKER_QUERY_STORAGE_STORE_BODY_LEN,</td></tr><tr><td class="line">106</td><td class="hits"></td><td class="source">        function(err, body) {</td></tr><tr class="hit"><td class="line">107</td><td class="hits">8</td><td class="source">            if (null != err) {</td></tr><tr class="miss"><td class="line">108</td><td class="hits">0</td><td class="source">                callback(err)</td></tr><tr class="miss"><td class="line">109</td><td class="hits">0</td><td class="source">                return</td></tr><tr><td class="line">110</td><td class="hits"></td><td class="source">            }</td></tr><tr><td class="line">111</td><td class="hits"></td><td class="source"> </td></tr><tr class="hit"><td class="line">112</td><td class="hits">8</td><td class="source">            var storageConfig = _parseStorage(body, self.config.charset, true)</td></tr><tr class="hit"><td class="line">113</td><td class="hits">8</td><td class="source">            storageConfig.timeout = self.config.timeout</td></tr><tr class="hit"><td class="line">114</td><td class="hits">8</td><td class="source">            storageConfig.charset = self.config.charset</td></tr><tr class="hit"><td class="line">115</td><td class="hits">8</td><td class="source">            var storage = new Storage(storageConfig)</td></tr><tr class="hit"><td class="line">116</td><td class="hits">8</td><td class="source">            logger.debug('get store storage server info: %j ', storage.config)</td></tr><tr class="hit"><td class="line">117</td><td class="hits">8</td><td class="source">            callback(null, storage)</td></tr><tr><td class="line">118</td><td class="hits"></td><td class="source">        }</td></tr><tr><td class="line">119</td><td class="hits"></td><td class="source">    )</td></tr><tr><td class="line">120</td><td class="hits"></td><td class="source">}</td></tr><tr><td class="line">121</td><td class="hits"></td><td class="source"> </td></tr><tr><td class="line">122</td><td class="hits"></td><td class="source">/**</td></tr><tr><td class="line">123</td><td class="hits"></td><td class="source"> * query which storage server to download the file</td></tr><tr><td class="line">124</td><td class="hits"></td><td class="source"> * 若只传1个参数则认为是fileId</td></tr><tr><td class="line">125</td><td class="hits"></td><td class="source"> *</td></tr><tr><td class="line">126</td><td class="hits"></td><td class="source"> * * TRACKER_PROTO_CMD_SERVICE_QUERY_FETCH_ONE</td></tr><tr><td class="line">127</td><td class="hits"></td><td class="source">     # function: query which storage server to download the file</td></tr><tr><td class="line">128</td><td class="hits"></td><td class="source">     # request body:</td></tr><tr><td class="line">129</td><td class="hits"></td><td class="source">     @ FDFS_GROUP_NAME_MAX_LEN bytes: group name</td></tr><tr><td class="line">130</td><td class="hits"></td><td class="source">     @ filename bytes: filename</td></tr><tr><td class="line">131</td><td class="hits"></td><td class="source"> </td></tr><tr><td class="line">132</td><td class="hits"></td><td class="source">     # response body:</td></tr><tr><td class="line">133</td><td class="hits"></td><td class="source">     @ FDFS_GROUP_NAME_MAX_LEN bytes: group name</td></tr><tr><td class="line">134</td><td class="hits"></td><td class="source">     @ FDFS_IPADDR_SIZE - 1 bytes: storage server ip address</td></tr><tr><td class="line">135</td><td class="hits"></td><td class="source">     @ FDFS_PROTO_PKG_LEN_SIZE bytes: storage server port</td></tr><tr><td class="line">136</td><td class="hits"></td><td class="source"> *</td></tr><tr><td class="line">137</td><td class="hits"></td><td class="source"> * @param group</td></tr><tr><td class="line">138</td><td class="hits"></td><td class="source"> * @param filename</td></tr><tr><td class="line">139</td><td class="hits"></td><td class="source"> */</td></tr><tr class="hit"><td class="line">140</td><td class="hits">1</td><td class="source">Tracker.prototype.getFetchStorage = function(group, filename, callback) {</td></tr><tr class="hit"><td class="line">141</td><td class="hits">3</td><td class="source">    logger.debug('get a fetch storage server from tracker server: [%s]', this._name)</td></tr><tr class="hit"><td class="line">142</td><td class="hits">3</td><td class="source">    this.getFetchOrUpdateStorage(protocol.TRACKER_PROTO_CMD_SERVICE_QUERY_FETCH_ONE, group, filename, callback)</td></tr><tr><td class="line">143</td><td class="hits"></td><td class="source">}</td></tr><tr><td class="line">144</td><td class="hits"></td><td class="source"> </td></tr><tr class="hit"><td class="line">145</td><td class="hits">1</td><td class="source">Tracker.prototype.getUpdateStorage = function(group, filename, callback) {</td></tr><tr class="hit"><td class="line">146</td><td class="hits">16</td><td class="source">    logger.debug('get a update storage server from tracker server: [%s]', this._name)</td></tr><tr class="hit"><td class="line">147</td><td class="hits">16</td><td class="source">    this.getFetchOrUpdateStorage(protocol.TRACKER_PROTO_CMD_SERVICE_QUERY_UPDATE, group, filename, callback)</td></tr><tr><td class="line">148</td><td class="hits"></td><td class="source">}</td></tr><tr><td class="line">149</td><td class="hits"></td><td class="source"> </td></tr><tr><td class="line">150</td><td class="hits"></td><td class="source">/**</td></tr><tr><td class="line">151</td><td class="hits"></td><td class="source"> * # request body:</td></tr><tr><td class="line">152</td><td class="hits"></td><td class="source">     @ FDFS_GROUP_NAME_MAX_LEN bytes: group name</td></tr><tr><td class="line">153</td><td class="hits"></td><td class="source">     @ filename bytes: filename</td></tr><tr><td class="line">154</td><td class="hits"></td><td class="source"> </td></tr><tr><td class="line">155</td><td class="hits"></td><td class="source">   # response body:</td></tr><tr><td class="line">156</td><td class="hits"></td><td class="source">     @ FDFS_GROUP_NAME_MAX_LEN bytes: group name</td></tr><tr><td class="line">157</td><td class="hits"></td><td class="source">     @ FDFS_IPADDR_SIZE - 1 bytes: storage server ip address</td></tr><tr><td class="line">158</td><td class="hits"></td><td class="source">     @ FDFS_PROTO_PKG_LEN_SIZE bytes: storage server port</td></tr><tr><td class="line">159</td><td class="hits"></td><td class="source"> * @param command TRACKER_PROTO_CMD_SERVICE_QUERY_FETCH_ONE or TRACKER_PROTO_CMD_SERVICE_QUERY_UPDATE</td></tr><tr><td class="line">160</td><td class="hits"></td><td class="source"> * @param group</td></tr><tr><td class="line">161</td><td class="hits"></td><td class="source"> * @param filename</td></tr><tr><td class="line">162</td><td class="hits"></td><td class="source"> * @param callback</td></tr><tr><td class="line">163</td><td class="hits"></td><td class="source"> * @private</td></tr><tr><td class="line">164</td><td class="hits"></td><td class="source"> */</td></tr><tr class="hit"><td class="line">165</td><td class="hits">1</td><td class="source">Tracker.prototype.getFetchOrUpdateStorage = function(command, group, filename, callback) {</td></tr><tr class="hit"><td class="line">166</td><td class="hits">19</td><td class="source">    if (util.isFunction(filename)) {</td></tr><tr class="hit"><td class="line">167</td><td class="hits">19</td><td class="source">        callback = filename</td></tr><tr class="hit"><td class="line">168</td><td class="hits">19</td><td class="source">        var gf = helpers.id2gf(group)</td></tr><tr class="hit"><td class="line">169</td><td class="hits">19</td><td class="source">        group = gf.group</td></tr><tr class="hit"><td class="line">170</td><td class="hits">19</td><td class="source">        filename = gf.filename</td></tr><tr><td class="line">171</td><td class="hits"></td><td class="source">    }</td></tr><tr class="hit"><td class="line">172</td><td class="hits">19</td><td class="source">    logger.debug('group: %s, filename: %s', group, filename)</td></tr><tr class="hit"><td class="line">173</td><td class="hits">19</td><td class="source">    var self = this</td></tr><tr class="hit"><td class="line">174</td><td class="hits">19</td><td class="source">    var socket = this._getConnection()</td></tr><tr class="hit"><td class="line">175</td><td class="hits">19</td><td class="source">    socket.on('connect', function() {</td></tr><tr class="hit"><td class="line">176</td><td class="hits">19</td><td class="source">        var packet = protocol.packFileId(command, group, filename, self.config.charset)</td></tr><tr class="hit"><td class="line">177</td><td class="hits">19</td><td class="source">        socket.write(packet)</td></tr><tr><td class="line">178</td><td class="hits"></td><td class="source">    })</td></tr><tr><td class="line">179</td><td class="hits"></td><td class="source"> </td></tr><tr class="hit"><td class="line">180</td><td class="hits">19</td><td class="source">    protocol.recvPacket(</td></tr><tr><td class="line">181</td><td class="hits"></td><td class="source">        socket,</td></tr><tr><td class="line">182</td><td class="hits"></td><td class="source">        protocol.TRACKER_PROTO_CMD_RESP,</td></tr><tr><td class="line">183</td><td class="hits"></td><td class="source">        protocol.FDFS_GROUP_NAME_MAX_LEN + protocol.FDFS_IPADDR_SIZE - 1 + protocol.FDFS_PROTO_PKG_LEN_SIZE,</td></tr><tr><td class="line">184</td><td class="hits"></td><td class="source">        function(err, body) {</td></tr><tr class="hit"><td class="line">185</td><td class="hits">19</td><td class="source">            if (null != err) {</td></tr><tr class="miss"><td class="line">186</td><td class="hits">0</td><td class="source">                callback(err)</td></tr><tr class="miss"><td class="line">187</td><td class="hits">0</td><td class="source">                return</td></tr><tr><td class="line">188</td><td class="hits"></td><td class="source">            }</td></tr><tr class="hit"><td class="line">189</td><td class="hits">19</td><td class="source">            var storageConfig = _parseStorage(body, self.config.charset)</td></tr><tr class="hit"><td class="line">190</td><td class="hits">19</td><td class="source">            storageConfig.timeout = self.config.timeout</td></tr><tr class="hit"><td class="line">191</td><td class="hits">19</td><td class="source">            storageConfig.charset = self.config.charset</td></tr><tr class="hit"><td class="line">192</td><td class="hits">19</td><td class="source">            var storage = new Storage(storageConfig)</td></tr><tr class="hit"><td class="line">193</td><td class="hits">19</td><td class="source">            logger.info('get storage server info: %j ', storage.config)</td></tr><tr class="hit"><td class="line">194</td><td class="hits">19</td><td class="source">            callback(null, storage)</td></tr><tr><td class="line">195</td><td class="hits"></td><td class="source">        }</td></tr><tr><td class="line">196</td><td class="hits"></td><td class="source">    )</td></tr><tr><td class="line">197</td><td class="hits"></td><td class="source">}</td></tr><tr><td class="line">198</td><td class="hits"></td><td class="source"> </td></tr><tr><td class="line">199</td><td class="hits"></td><td class="source">// -------------- helper methods</td></tr><tr><td class="line">200</td><td class="hits"></td><td class="source">/**</td></tr><tr><td class="line">201</td><td class="hits"></td><td class="source"> *   @ FDFS_GROUP_NAME_MAX_LEN bytes: group name</td></tr><tr><td class="line">202</td><td class="hits"></td><td class="source"> *   @ FDFS_IPADDR_SIZE - 1 bytes: storage server ip address</td></tr><tr><td class="line">203</td><td class="hits"></td><td class="source"> *   @ FDFS_PROTO_PKG_LEN_SIZE bytes: storage server port</td></tr><tr><td class="line">204</td><td class="hits"></td><td class="source"> *   @1 byte: store path index on the storage server {可以没有这个字节}</td></tr><tr><td class="line">205</td><td class="hits"></td><td class="source"> *</td></tr><tr><td class="line">206</td><td class="hits"></td><td class="source"> * @param {Buffer} body</td></tr><tr><td class="line">207</td><td class="hits"></td><td class="source"> * @param {String} charset</td></tr><tr><td class="line">208</td><td class="hits"></td><td class="source"> * @param {Boolean} hasPathIndex</td></tr><tr><td class="line">209</td><td class="hits"></td><td class="source"> * @private {Object}</td></tr><tr><td class="line">210</td><td class="hits"></td><td class="source"> */</td></tr><tr class="hit"><td class="line">211</td><td class="hits">1</td><td class="source">function _parseStorage(body, charset, hasPathIndex) {</td></tr><tr class="hit"><td class="line">212</td><td class="hits">27</td><td class="source">    var result = {}</td></tr><tr><td class="line">213</td><td class="hits"></td><td class="source"> </td></tr><tr class="hit"><td class="line">214</td><td class="hits">27</td><td class="source">    var group = helpers.trim(body.toString(charset, 0, protocol.FDFS_GROUP_NAME_MAX_LEN))</td></tr><tr class="hit"><td class="line">215</td><td class="hits">27</td><td class="source">    var ip = helpers.trim(body.toString(charset, protocol.FDFS_GROUP_NAME_MAX_LEN, protocol.FDFS_GROUP_NAME_MAX_LEN + protocol.FDFS_IPADDR_SIZE - 1))</td></tr><tr class="hit"><td class="line">216</td><td class="hits">27</td><td class="source">    var port = Number('0x' + body.toString('hex',</td></tr><tr><td class="line">217</td><td class="hits"></td><td class="source">        protocol.FDFS_GROUP_NAME_MAX_LEN + protocol.FDFS_IPADDR_SIZE - 1,</td></tr><tr><td class="line">218</td><td class="hits"></td><td class="source">        protocol.FDFS_GROUP_NAME_MAX_LEN + protocol.FDFS_IPADDR_SIZE - 1 + protocol.FDFS_PROTO_PKG_LEN_SIZE))</td></tr><tr><td class="line">219</td><td class="hits"></td><td class="source"> </td></tr><tr class="hit"><td class="line">220</td><td class="hits">27</td><td class="source">    result.group = group</td></tr><tr class="hit"><td class="line">221</td><td class="hits">27</td><td class="source">    result.host = ip</td></tr><tr class="hit"><td class="line">222</td><td class="hits">27</td><td class="source">    result.port = port</td></tr><tr><td class="line">223</td><td class="hits"></td><td class="source"> </td></tr><tr class="hit"><td class="line">224</td><td class="hits">27</td><td class="source">    if (hasPathIndex &amp;&amp;</td></tr><tr><td class="line">225</td><td class="hits"></td><td class="source">        body.length &gt; protocol.FDFS_GROUP_NAME_MAX_LEN + protocol.FDFS_IPADDR_SIZE - 1 + protocol.FDFS_PROTO_PKG_LEN_SIZE) {</td></tr><tr class="hit"><td class="line">226</td><td class="hits">8</td><td class="source">        var storePathIndex = Number('0x' + body.toString('hex',</td></tr><tr><td class="line">227</td><td class="hits"></td><td class="source">            protocol.FDFS_GROUP_NAME_MAX_LEN + protocol.FDFS_IPADDR_SIZE - 1 + protocol.FDFS_PROTO_PKG_LEN_SIZE,</td></tr><tr><td class="line">228</td><td class="hits"></td><td class="source">            protocol.FDFS_GROUP_NAME_MAX_LEN + protocol.FDFS_IPADDR_SIZE - 1 + protocol.FDFS_PROTO_PKG_LEN_SIZE + 1))</td></tr><tr><td class="line">229</td><td class="hits"></td><td class="source"> </td></tr><tr class="hit"><td class="line">230</td><td class="hits">8</td><td class="source">        result.storePathIndex = storePathIndex</td></tr><tr><td class="line">231</td><td class="hits"></td><td class="source">    }</td></tr><tr><td class="line">232</td><td class="hits"></td><td class="source"> </td></tr><tr class="hit"><td class="line">233</td><td class="hits">27</td><td class="source">    return result</td></tr><tr><td class="line">234</td><td class="hits"></td><td class="source">}</td></tr><tr><td class="line">235</td><td class="hits"></td><td class="source"> </td></tr><tr class="hit"><td class="line">236</td><td class="hits">1</td><td class="source">module.exports = Tracker</td></tr><tr><td class="line">237</td><td class="hits"></td><td class="source"> </td></tr></tbody></table></div></div></div></body></html>